diff --git a/.vscode/launch.json b/.vscode/launch.json
index e2826c6c69..0ad59dd7dd 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -4,21 +4,1628 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
+ {
+ "type": "java",
+ "name": "Current File",
+ "request": "launch",
+ "mainClass": "${file}"
+ },
+ {
+ "type": "java",
+ "name": "PeerDiscovery",
+ "request": "launch",
+ "mainClass": "PeerDiscovery",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ArduinoMsgGenerator",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.arduino.ArduinoMsgGenerator",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Msg",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.arduino.Msg",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "VirtualMsg",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.arduino.VirtualMsg",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "CaptureCalibrationImagesApp",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.CaptureCalibrationImagesApp",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "CreateRgbPointCloudFileApp",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.CreateRgbPointCloudFileApp",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "DisplayKinectPointCloudApp",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.DisplayKinectPointCloudApp",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ExampleDepthPointCloud",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.ExampleDepthPointCloud",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ExampleVisualOdometryDepth",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.ExampleVisualOdometryDepth",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "IntrinsicToDepthParameters",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.IntrinsicToDepthParameters",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "LogKinectDataApp",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.LogKinectDataApp",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenKinectOdometry",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.OpenKinectOdometry",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenKinectPointCloud",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.OpenKinectPointCloud",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenKinectStreamingTest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.OpenKinectStreamingTest",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OverlayRgbDepthStreamsApp",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.OverlayRgbDepthStreamsApp",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "PlaybackKinectLogApp",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.boofcv.PlaybackKinectLogApp",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "CmdLine",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.cmdline.CmdLine",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "CodecUtils",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.codec.CodecUtils",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "PolymorphicSerializer",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.codec.PolymorphicSerializer",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "RecorderPythonFile",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.codec.RecorderPythonFile",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "CvData",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.cv.CvData",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WikipediaIndexer",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.document.connector.WikipediaIndexer",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Index",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.Index",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MRLListener",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.MRLListener",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Message",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.Message",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MethodCacheTest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.MethodCacheTest",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "NameGenerator",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.NameGenerator",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Platform",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.Platform",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Status",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.Status",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TypeConverter",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.TypeConverter",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "IvyWrapper",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.repo.IvyWrapper",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MavenWrapper",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.repo.MavenWrapper",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ServiceData",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.framework.repo.ServiceData",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "I2CFactory",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.i2c.I2CFactory",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "SerializableImage",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.image.SerializableImage",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "FileIO",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.io.FileIO",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "FindFile",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.io.FindFile",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Zip",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.io.Zip",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TestPhysicsHingeJoint",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.jme3.TestPhysicsHingeJoint",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TestSimplePhysics",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.jme3.TestSimplePhysics",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TestJmeIMModel",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.kinematics.TestJmeIMModel",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TestJmeIntegratedMovement",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.kinematics.TestJmeIntegratedMovement",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MapperLinearTest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.math.MapperLinearTest",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Node",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.memory.Node",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Email",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.Email",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Http",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.Http",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "HttpRequest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.HttpRequest",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "InstallCert",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.InstallCert",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MjpegServer",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.MjpegServer",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "NanoHTTPD",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.NanoHTTPD",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TcpSerialHub",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.TcpSerialHub",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TcpServer",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.TcpServer",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WsClient",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.net.WsClient",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OculusDisplay",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.oculus.OculusDisplay",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenCVData",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.opencv.OpenCVData",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenCVFaceRecognizer",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.opencv.OpenCVFaceRecognizer",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenCVFilterAddMask",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.opencv.OpenCVFilterAddMask",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenCVFilterKinectFloorFinder",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.opencv.OpenCVFilterKinectFloorFinder",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenCVFilterKinectPointCloud",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.opencv.OpenCVFilterKinectPointCloud",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "InProcessCli",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.process.InProcessCli",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Launcher",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.process.Launcher",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "RTTTLParser",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.roomba.RTTTLParser",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "RTTTLPlay",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.roomba.RTTTLPlay",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ProcParser",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.runtime.ProcParser",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Adafruit16CServoDriver",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Adafruit16CServoDriver",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "AdafruitIna219",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.AdafruitIna219",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "AdafruitMotorHat4Pi",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.AdafruitMotorHat4Pi",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Ads1115",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Ads1115",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Amt203Encoder",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Amt203Encoder",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Android",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Android",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Arduino",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Arduino",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Arm",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Arm",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "As5048AEncoder",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.As5048AEncoder",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "AudioCapture",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.AudioCapture",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "AudioFile",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.AudioFile",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "AzureTranslator",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.AzureTranslator",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "BeagleBoardBlack",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.BeagleBoardBlack",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Blender",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Blender",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Blocks",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Blocks",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Bno055",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Bno055",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "BodyPart",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.BodyPart",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "BoofCv",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.BoofCv",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Chassis",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Chassis",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ChessGame",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.ChessGame",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Clock",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Clock",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Cron",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Cron",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Database",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Database",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Deeplearning4j",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Deeplearning4j",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "DiscordBot",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.DiscordBot",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "DiyServo",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.DiyServo",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Docker",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Docker",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "DocumentPipeline",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.DocumentPipeline",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "DruppNeck",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.DruppNeck",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "EddieControlBoard",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.EddieControlBoard",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Elasticsearch",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Elasticsearch",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Emoji",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Emoji",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Esp8266",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Esp8266",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Esp8266_01",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Esp8266_01",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "FiniteStateMachine",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.FiniteStateMachine",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Git",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Git",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "GoPro",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.GoPro",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "GoogleCloud",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.GoogleCloud",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "GoogleSearch",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.GoogleSearch",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "GoogleTranslate",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.GoogleTranslate",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Gps",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Gps",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Gpt3",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Gpt3",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Hd44780",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Hd44780",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "HtmlFilter",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.HtmlFilter",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "HtmlParser",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.HtmlParser",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "HttpClient",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.HttpClient",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "I2cMux",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.I2cMux",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "IBus",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.IBus",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ImageDisplay",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.ImageDisplay",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ImapEmailConnector",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.ImapEmailConnector",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "InMoov2",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.InMoov2",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "InMoov2Hand",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.InMoov2Hand",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "InMoov2Head",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.InMoov2Head",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "InMoov2Torso",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.InMoov2Torso",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "IndianTts",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.IndianTts",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "IntegratedMovement",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.IntegratedMovement",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Intro",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Intro",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "InverseKinematics",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.InverseKinematics",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "InverseKinematics3D",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.InverseKinematics3D",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "IpCamera",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.IpCamera",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "JFugue",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.JFugue",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "JMonkeyEngine",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.JMonkeyEngine",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "JMonkeyEngineTest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.JMonkeyEngineTest",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "JavaScript",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.JavaScript",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Joystick",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Joystick",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "KafkaConnector",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.KafkaConnector",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Keyboard",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Keyboard",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "KeyboardSim",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.KeyboardSim",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "LeapMotion",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.LeapMotion",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "LeapMotion2",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.LeapMotion2",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Lidar",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Lidar",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "LidarVlp16",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.LidarVlp16",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Lloyd",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Lloyd",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Lm75a",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Lm75a",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "LocalSpeech",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.LocalSpeech",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Log",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Log",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Mail",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Mail",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MarySpeech",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MarySpeech",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Maven",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Maven",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MobilePlatform",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MobilePlatform",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Motor",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Motor",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MotorDualPwm",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MotorDualPwm",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MotorHat4Pi",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MotorHat4Pi",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MotorPort",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MotorPort",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MouseSim",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MouseSim",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MouthControl",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MouthControl",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Mpr121",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Mpr121",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Mpu6050",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Mpu6050",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Mqtt",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Mqtt",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MqttBroker",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MqttBroker",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MultiWii",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MultiWii",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "MyoThalmic",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.MyoThalmic",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "NeoPixel",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.NeoPixel",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OakD",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.OakD",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OculusDiy",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.OculusDiy",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OculusRift",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.OculusRift",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OledSsd1306",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.OledSsd1306",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenCV",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.OpenCV",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenCVTest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.OpenCVTest",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenNi",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.OpenNi",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "OpenWeatherMap",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.OpenWeatherMap",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Osc",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Osc",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Pcf8574",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Pcf8574",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Pid",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Pid",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Pingdar",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Pingdar",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Pir",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Pir",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Polly",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Polly",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ProgramAB",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.ProgramAB",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Py4j",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Py4j",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Python",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Python",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "RSSConnector",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.RSSConnector",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Random",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Random",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "RasPi",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.RasPi",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Rekognition",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Rekognition",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Relay",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Relay",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "RoboClaw",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.RoboClaw",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Roomba",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Roomba",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Ros",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Ros",
+ "projectName": "mrl"
+ },
{
"type": "java",
"name": "Runtime",
"request": "launch",
"mainClass": "org.myrobotlab.service.Runtime",
- "projectName": "mrl",
- "args": [
- "--log-level",
- "info",
- "-s",
- "webgui", "WebGui", "intro", "Intro", "python", "Python",
- "-c",
- "dev"
- ]
-
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Sabertooth",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Sabertooth",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Security",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Security",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "SegmentDisplay",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.SegmentDisplay",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "SensorMonitor",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.SensorMonitor",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Serial",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Serial",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "SerialRelay",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.SerialRelay",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Servo",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Servo",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ServoMixer",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.ServoMixer",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Shoutbox",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Shoutbox",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "SlackBot",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.SlackBot",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Solr",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Solr",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Sphinx",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Sphinx",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "SpotMicro",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.SpotMicro",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Ssc32UsbServoController",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Ssc32UsbServoController",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TarsosDsp",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.TarsosDsp",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Tensorflow",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Tensorflow",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TesseractOcr",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.TesseractOcr",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Test",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Test",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TestCatcher",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.TestCatcher",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "ThingSpeak",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.ThingSpeak",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "TopCodes",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.TopCodes",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Tracking",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Tracking",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Twitter",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Twitter",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "UltrasonicSensor",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.UltrasonicSensor",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "UltrasonicSensorTest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.UltrasonicSensorTest",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Updater",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Updater",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "VideoStreamer",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.VideoStreamer",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "VirtualArduino",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.VirtualArduino",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "VoiceRss",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.VoiceRss",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WatchDogTimer",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WatchDogTimer",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WebGui",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WebGui",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WebSocketConnector",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WebSocketConnector",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Webcam",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Webcam",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WebkitSpeechRecognition",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WebkitSpeechRecognition",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WebkitSpeechSynthesis",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WebkitSpeechSynthesis",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Wii",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Wii",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WiiDar",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WiiDar",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WikiDataFetcher",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WikiDataFetcher",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Wikipedia",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Wikipedia",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WolframAlpha",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WolframAlpha",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "WorkE",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.WorkE",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Xmpp",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.Xmpp",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "YahooFinanceStockQuote",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.YahooFinanceStockQuote",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "_TemplateService",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service._TemplateService",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "Pin",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.data.Pin",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "SpeechSynthesisTest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.service.interfaces.SpeechSynthesisTest",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "StringUtil",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.string.StringUtil",
+ "projectName": "mrl"
+ },
+ {
+ "type": "java",
+ "name": "AbstractTest",
+ "request": "launch",
+ "mainClass": "org.myrobotlab.test.AbstractTest",
+ "projectName": "mrl"
}
]
}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index c0d2e88e12..651c18245e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -17,7 +17,7 @@
"jest.coverageFormatter": "GutterFormatter",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
- "source.organizeImports": true
+ "source.organizeImports": "explicit"
},
"typescript.tsdk": "./app/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
diff --git a/Jenkinsfile b/Jenkinsfile
index afe77e3c30..d319ec5061 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -11,6 +11,10 @@ pipeline {
// agent { label params['agent-name'] }
agent any
+ triggers {
+ cron('H 0 * * *')
+ }
+
parameters {
// agentParameter name:'agent-name'
choice(name: 'verify', choices: ['true', 'false'], description: 'verify')
@@ -83,7 +87,7 @@ pipeline {
steps {
script {
sh '''
- mvn -Dfile.encoding=UTF-8 -Dversion=${VERSION} clean package jacoco:report -q
+ mvn -Dfile.encoding=UTF-8 -Dbuild.number=${VERSION} clean package jacoco:report surefire-report:report -q
'''
}
}
diff --git a/README.md b/README.md
index 69e1433cbf..7e300606e0 100644
--- a/README.md
+++ b/README.md
@@ -286,4 +286,3 @@ Services returned from the Describe request have been registered. By default th
The response should be refactored so that the material returned is related to criterial requested.
For example, if only a list of services and their names are needed, that is all that is returned. If a specific service's interfaces are requested, then that is only returned.
Fill-UUID should be refactored out.
-
diff --git a/pom.xml b/pom.xml
index 9b89a805fc..bb69ed4b07 100644
--- a/pom.xml
+++ b/pom.xml
@@ -184,6 +184,27 @@
+
+
+ au.edu.federation.caliko
+ caliko
+ 1.3.8
+ provided
+
+
+ au.edu.federation.caliko.visualisation
+ caliko-visualisation
+ 1.3.8
+ provided
+
+
+ au.edu.federation.caliko.demo
+ caliko-demo
+ 1.3.8
+ provided
+
+
+
ChessBoard
@@ -484,10 +505,6 @@
-
-
-
-
jfugue
@@ -1271,6 +1288,12 @@
9.4.2provided
+
+ org.openjdk.nashorn
+ nashorn-core
+ 15.4
+ provided
+
@@ -2024,7 +2047,7 @@
**/integration/*
-
+
vector1, Vector vector2) {
+ // Check if the vectors have the same dimension
+ if (vector1.size() != vector2.size()) {
+ throw new IllegalArgumentException("Vectors must have the same dimension");
+ }
+
+ // Calculate the dot product of the vectors
+ double dotProduct = 0.0;
+ double magnitude1 = 0.0;
+ double magnitude2 = 0.0;
+
+ for (int i = 0; i < vector1.size(); i++) {
+ dotProduct += vector1.get(i) * vector2.get(i);
+ magnitude1 += Math.pow(vector1.get(i), 2);
+ magnitude2 += Math.pow(vector2.get(i), 2);
+ }
+
+ magnitude1 = Math.sqrt(magnitude1);
+ magnitude2 = Math.sqrt(magnitude2);
+
+ // Calculate the angle in radians
+ double radians = Math.acos(dotProduct / (magnitude1 * magnitude2));
+
+ // Convert radians to degrees
+ double degrees = Math.toDegrees(radians);
+
+ return degrees;
+ }
+
+ public static void main(String[] args) {
+ Vector vector1 = new Vector<>();
+ vector1.add(1.0);
+ vector1.add(0.0);
+
+ Vector vector2 = new Vector<>();
+ vector2.add(1.0);
+ vector2.add(1.0);
+
+ double angleDegrees = calculateAngle(vector1, vector2);
+ System.out.println("Angle between vectors: " + angleDegrees + " degrees");
+ }
+}
diff --git a/src/main/java/org/myrobotlab/arduino/VirtualMsg.java b/src/main/java/org/myrobotlab/arduino/VirtualMsg.java
index cc29874af4..d2cc3ddaf7 100644
--- a/src/main/java/org/myrobotlab/arduino/VirtualMsg.java
+++ b/src/main/java/org/myrobotlab/arduino/VirtualMsg.java
@@ -3,6 +3,8 @@
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
+import java.io.IOException;
+import org.myrobotlab.service.Serial;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
@@ -44,8 +46,11 @@ b16 int int (short) 2 bytes -32,768 to 32,767
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.LoggingFactory;
+import org.myrobotlab.service.VirtualArduino;
import java.io.FileOutputStream;
+import java.util.Arrays;
+import org.myrobotlab.service.interfaces.MrlCommPublisher;
import org.myrobotlab.service.Runtime;
import org.myrobotlab.service.Servo;
import org.myrobotlab.service.interfaces.SerialDevice;
@@ -1589,7 +1594,9 @@ public void onBytes(byte[] bytes) {
msgSize = 0;
Arrays.fill(ioCmd, 0); // FIXME - optimize - remove
// warn(String.format("Arduino->MRL error - bad magic number %d - %d rx errors", newByte, ++errorServiceToHardwareRxCnt));
- log.warn("Arduino->MRL error - bad magic number {} - {} rx errors", newByte, ++errorServiceToHardwareRxCnt);
+ if (!arduino.isConnecting()){
+ log.warn("Arduino->MRL error - bad magic number {} - {} rx errors", newByte, ++errorServiceToHardwareRxCnt);
+ }
}
continue;
} else if (byteCount.get() == 2) {
@@ -1622,7 +1629,10 @@ public void onBytes(byte[] bytes) {
}
if (!clearToSend) {
- log.warn("NOT CLEAR TO SEND! resetting parser!");
+ if (!arduino.isConnecting()) {
+ // we're connecting, so we're going to ignore the message.
+ log.warn("NOT CLEAR TO SEND! resetting parser!");
+ }
// We opened the port, and we got some data that isn't a Begin message.
// so, I think we need to reset the parser and continue processing bytes...
// there will be errors until the next magic byte is seen.
@@ -1877,7 +1887,7 @@ public void enableAcks(boolean b){
}
public void waitForAck(){
- if (!ackEnabled) {
+ if (!ackEnabled || serial == null || !serial.isConnected()) {
return;
}
// if there's a pending message, we need to wait for the ack to be received.
@@ -1910,10 +1920,8 @@ public int getMethod(){
public void add(int value) {
- // this explodes - sendBufferSize increases forever ... duh index not valid
- // is this suppose to be round robin buffer ?
- // sendBuffer[sendBufferSize] = (value & 0xFF);
- // sendBufferSize += 1;
+ sendBuffer[sendBufferSize] = (value & 0xFF);
+ sendBufferSize += 1;
}
public int[] getBuffer() {
@@ -2019,4 +2027,8 @@ public void setInvoke(boolean b){
invoke = b;
}
+ public void setSerial(Serial serial) {
+ this.serial = serial;
+ }
+
}
diff --git a/src/main/java/org/myrobotlab/caliko/Application.java b/src/main/java/org/myrobotlab/caliko/Application.java
new file mode 100644
index 0000000000..d8474c157e
--- /dev/null
+++ b/src/main/java/org/myrobotlab/caliko/Application.java
@@ -0,0 +1,82 @@
+package org.myrobotlab.caliko;
+
+import static org.lwjgl.glfw.GLFW.glfwPollEvents;
+import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose;
+import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
+import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
+import static org.lwjgl.opengl.GL11.glClear;
+
+import org.myrobotlab.service.Caliko;
+
+import au.edu.federation.caliko.demo.CalikoDemo;
+import au.edu.federation.utils.Vec3f;
+
+/**
+ * An example application to demonstrate the Caliko library in both 2D and 3D
+ * modes.
+ *
+ * Use up/down cursors to change between 2D/3D mode and left/right cursors to
+ * change demos. In 2D mode clicking using the left mouse button (LMB) changes
+ * the target location, and you can click and drag. In 3D mode, use W/S/A/D to
+ * move the camera and the mouse with LMB held down to look.
+ *
+ * See the README.txt for further documentation and controls.
+ *
+ * @author Al Lansley
+ * @version 1.0 - 31/01/2016
+ */
+public class Application {
+ // Define cardinal axes
+ final Vec3f X_AXIS = new Vec3f(1.0f, 0.0f, 0.0f);
+ final Vec3f Y_AXIS = new Vec3f(0.0f, 1.0f, 0.0f);
+ final Vec3f Z_AXIS = new Vec3f(0.0f, 0.0f, 1.0f);
+
+ // State tracking variables
+ boolean use3dDemo = true;
+ int demoNumber = 7;
+ boolean fixedBaseMode = true;
+ boolean rotateBasesMode = false;
+ boolean drawLines = true;
+ boolean drawAxes = false;
+ boolean drawModels = true;
+ boolean drawConstraints = true;
+ boolean leftMouseButtonDown = false;
+ boolean paused = true;
+
+ // Create our window and OpenGL context
+ int windowWidth = 800;
+ int windowHeight = 600;
+ public OpenGLWindow window = null;
+
+ // Declare a CalikoDemo object which can run our 3D and 2D demonstration
+ // scenarios
+ transient private CalikoDemo demo;
+ transient private Caliko service;
+ public boolean running = true;
+
+ public Application(Caliko service) {
+ this.service = service;
+ window = new OpenGLWindow(this, service, windowWidth, windowHeight);
+ demo = new CalikoDemo3D(this);
+ mainLoop();
+ window.cleanup();
+ }
+
+ public Caliko getService() {
+ return service;
+ }
+
+ private void mainLoop() {
+ // Run the rendering loop until the user closes the window or presses Escape
+ while (!glfwWindowShouldClose(window.mWindowId) && running) {
+ // Clear the screen and depth buffer then draw the demo
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ demo.draw();
+
+ // Swap the front and back colour buffers and poll for events
+ window.swapBuffers();
+ glfwPollEvents();
+ }
+ }
+
+} // End of Application class
diff --git a/src/main/java/org/myrobotlab/caliko/CalikoDemo3D.java b/src/main/java/org/myrobotlab/caliko/CalikoDemo3D.java
new file mode 100644
index 0000000000..8e977fe9d7
--- /dev/null
+++ b/src/main/java/org/myrobotlab/caliko/CalikoDemo3D.java
@@ -0,0 +1,148 @@
+package org.myrobotlab.caliko;
+
+import au.edu.federation.caliko.FabrikBone3D;
+import au.edu.federation.caliko.FabrikChain3D;
+import au.edu.federation.caliko.FabrikStructure3D;
+import au.edu.federation.caliko.demo.CalikoDemo;
+import au.edu.federation.caliko.demo3d.CalikoDemoStructure3D;
+import au.edu.federation.caliko.visualisation.Axis;
+import au.edu.federation.caliko.visualisation.Camera;
+import au.edu.federation.caliko.visualisation.FabrikConstraint3D;
+import au.edu.federation.caliko.visualisation.FabrikLine3D;
+// import au.edu.federation.caliko.visualisation.FabrikModel3D;
+import au.edu.federation.caliko.visualisation.Grid;
+import au.edu.federation.caliko.visualisation.MovingTarget3D;
+import au.edu.federation.utils.Mat4f;
+import au.edu.federation.utils.Utils;
+import au.edu.federation.utils.Vec3f;
+
+/**
+ * Class to demonstrate some of the features of the Caliko library in 3D.
+ *
+ * @author Al Lansley
+ * @version 0.7.1 - 20/07/2016
+ */
+public class CalikoDemo3D implements CalikoDemo
+{
+ static float defaultBoneLength = 10.0f;
+ static float boneLineWidth = 5.0f;
+ static float constraintLineWidth = 2.0f;
+ static float baseRotationAmountDegs = 0.3f;
+
+ // Set yo a camera which we'll use to navigate. Params: location, orientation, width and height of window.
+ static Camera camera = new Camera(new Vec3f(0.0f, 00.0f, 150.0f), new Vec3f(), 800, 600);
+
+ // Setup some grids to aid orientation
+ static float extent = 1000.0f;
+ static float gridLevel = 100.0f;
+ static int subdivisions = 20;
+ static Grid lowerGrid = new Grid(extent, extent, -gridLevel, subdivisions);
+ static Grid upperGrid = new Grid(extent, extent, gridLevel, subdivisions);
+
+ // An axis to show the X/Y/Z orientation of each bone. Params: Axis length, axis line width
+ static Axis axis = new Axis(3.0f, 1.0f);
+
+ // A constraint we can use to draw any joint angle restrictions of ball and hinge joints
+ static FabrikConstraint3D constraint = new FabrikConstraint3D();
+
+ // A simple Wavefront .OBJ format model of a pyramid to display around each bone (set to draw with a 1.0f line width)
+ static FabrikModel3D model = new FabrikModel3D("/pyramid.obj", 1.0f);
+
+ // Setup moving target. Params: location, extents, interpolation frames, grid height for vertical bar
+ static MovingTarget3D target = new MovingTarget3D(new Vec3f(0, -30, 0), new Vec3f(60.0f), 200, gridLevel);
+
+ private FabrikStructure3D mStructure;
+
+ private CalikoDemoStructure3D demoStructure3d;
+
+ private transient Application application;
+
+ /**
+ * Constructor.
+ *
+ * @param demoNumber The number of the demo to set up.
+ */
+ public CalikoDemo3D(Application application)
+ {
+ this.application = application;
+ setup(0);
+ }
+
+ /**
+ * Set up a demo consisting of an arrangement of 3D IK chains with a given configuration.
+ *
+ * @param demoNumber The number of the demo to set up.
+ */
+ public void setup(int demoNumber)
+ {
+ this.mStructure = application.getService().getStructure();
+
+ this.demoStructure3d = new GuiDemoStructure(application.getService(), null);
+
+ // Set the appropriate window title and make an initial solve pass of the structure
+ application.window.setWindowTitle(this.mStructure.getName());
+ //structure.updateTarget( target.getCurrentLocation() );
+ }
+
+ /** Set all chains in the structure to be in fixed-base mode whereby the base locations cannot move. */
+ public void setFixedBaseMode(boolean value) { mStructure.setFixedBaseMode(value); }
+
+ /** Handle the movement of the camera using the W/S/A/D keys. */
+ public void handleCameraMovement(int key, int action) { camera.handleKeypress(key, action); }
+
+ public void draw()
+ {
+ // Move the camera based on keypresses and mouse movement
+ camera.move(1.0f / 60.0f);
+
+ // Get the ModelViewProjection matrix as we use it multiple times
+ Mat4f mvpMatrix = application.window.getMvpMatrix();
+
+ // Draw our grids
+ lowerGrid.draw(mvpMatrix);
+ upperGrid.draw(mvpMatrix);
+
+ // If we're not paused then step the target and solve the structure for the new target location
+ if (!application.paused)
+ {
+ target.step();
+ this.demoStructure3d.drawTarget(mvpMatrix);
+
+ // Solve the structure (chains with embedded targets will use those, otherwise the provided target is used)
+ mStructure.solveForTarget( target.getCurrentLocation() );
+
+ FabrikChain3D chain = application.getService().getChain("default");
+
+ for (FabrikBone3D bone : chain.getChain()) {
+ bone.getStartLocation().getGlobalPitchDegs();
+ bone.getStartLocation().getGlobalYawDegs();
+
+ System.out.println("Bone X: " + bone.getStartLocation().toString());
+ }
+
+ }
+
+ // If we're in rotate base mode then rotate the base location(s) of all chains in the structure
+ if (application.rotateBasesMode)
+ {
+ int numChains = mStructure.getNumChains();
+ for (int loop = 0; loop < numChains; ++loop)
+ {
+ Vec3f base = mStructure.getChain(loop).getBaseLocation();
+ base = Vec3f.rotateAboutAxisDegs(base, baseRotationAmountDegs, CalikoDemoStructure3D.Y_AXIS);
+ mStructure.getChain(loop).setBaseLocation(base);
+ }
+ }
+
+ // Draw the target
+ target.draw(Utils.YELLOW, 8.0f, mvpMatrix);
+
+ // Draw the structure as required
+ // Note: bone lines are drawn in the bone colour, models are drawn in white by default but you can specify a colour to the draw method,
+ // axes are drawn X/Y/Z as Red/Green/Blue and constraints are drawn the colours specified in the FabrikConstraint3D class.
+ if (application.drawLines) { FabrikLine3D.draw(mStructure, boneLineWidth, mvpMatrix); }
+ if (application.drawModels) { model.drawStructure(mStructure, camera.getViewMatrix(), application.window.mProjectionMatrix); }
+ if (application.drawAxes) { axis.draw(mStructure, camera.getViewMatrix(), application.window.mProjectionMatrix); }
+ if (application.drawConstraints) { constraint.draw(mStructure, constraintLineWidth, mvpMatrix); }
+ }
+}
diff --git a/src/main/java/org/myrobotlab/caliko/FabrikModel3D.java b/src/main/java/org/myrobotlab/caliko/FabrikModel3D.java
new file mode 100644
index 0000000000..470f98b01f
--- /dev/null
+++ b/src/main/java/org/myrobotlab/caliko/FabrikModel3D.java
@@ -0,0 +1,316 @@
+package org.myrobotlab.caliko;
+
+import java.nio.FloatBuffer;
+
+import au.edu.federation.caliko.FabrikBone3D;
+import au.edu.federation.caliko.FabrikChain3D;
+import au.edu.federation.caliko.FabrikStructure3D;
+import au.edu.federation.caliko.visualisation.ShaderProgram;
+import au.edu.federation.utils.Colour4f;
+import au.edu.federation.utils.Mat3f;
+import au.edu.federation.utils.Mat4f;
+import au.edu.federation.utils.Utils;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL15.*;
+import static org.lwjgl.opengl.GL20.*;
+import static org.lwjgl.opengl.GL30.*;
+
+/**
+ * A class to represent a 3D model that can easily be attached to a FabrikBone3D object.
+ *
+ * @author Al Lansley
+ * @version 0.3.1 - 20/07/2016
+ */
+public class FabrikModel3D
+{
+ // Each vertex has three positional components - the x, y and z values.
+ private static final int VERTEX_COMPONENTS = 3;
+
+ // A single static ShaderProgram is used to draw all axes
+ private static ShaderProgram shaderProgram;
+
+ // Vertex shader source
+ private static final String VERTEX_SHADER_SOURCE =
+ "#version 330" + Utils.NEW_LINE +
+ "in vec3 vertexLocation; // Incoming vertex attribute" + Utils.NEW_LINE +
+ "uniform mat4 mvpMatrix; // Combined Model/View/Projection matrix " + Utils.NEW_LINE +
+ "void main(void) {" + Utils.NEW_LINE +
+ " gl_Position = mvpMatrix * vec4(vertexLocation, 1.0); // Project our geometry" + Utils.NEW_LINE +
+ "}";
+
+ // Fragment shader source
+ private static final String FRAGMENT_SHADER_SOURCE =
+ "#version 330" + Utils.NEW_LINE +
+ "out vec4 outputColour;" + Utils.NEW_LINE +
+ "uniform vec4 colour;" + Utils.NEW_LINE +
+ "void main() {" + Utils.NEW_LINE +
+ " outputColour = colour;" + Utils.NEW_LINE +
+ "}";
+
+ // Hold id values for the Vertex Array Object (VAO) and Vertex Buffer Object (VBO)
+ private static int vaoId;
+ private static int vboId;
+
+ // Float buffers for the ModelViewProjection matrix and model colour
+ private static FloatBuffer mvpMatrixFB;
+ private static FloatBuffer colourFB;
+
+ // We'll keep track of and restore the current OpenGL line width, which we'll store in this FloatBuffer.
+ // Note: Although we only need a single float for this, LWJGL insists upon a minimum size of 16 floats.
+ private static FloatBuffer currentLineWidthFB;
+
+ // ----- Non-Static Properties -----
+
+ // The FloatBuffer which will contain our vertex data - as we may load multiple different models this cannot be static
+ private FloatBuffer vertexFB;
+
+ /** The actual Model associated with this FabrikModel3D. */
+ private Model model;
+
+ /** The float array storing the axis vertex (including colour) data. */
+ private float[] modelData;
+
+ /**
+ * The line width with which to draw the model in pixels.
+ *
+ * @default 1.0f
+ */
+ private float mLineWidth = 1.0f;
+
+ static {
+ mvpMatrixFB = Utils.createFloatBuffer(16);
+ colourFB = Utils.createFloatBuffer(16);
+ currentLineWidthFB = Utils.createFloatBuffer(16);
+
+ // ----- Grid shader program setup -----
+
+ shaderProgram = new ShaderProgram();
+ shaderProgram.initFromStrings(VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);
+
+ // ----- Grid shader attributes and uniforms -----
+
+ // Add the shader attributes and uniforms
+ shaderProgram.addAttribute("vertexLocation");
+ shaderProgram.addUniform("mvpMatrix");
+ shaderProgram.addUniform("colour");
+
+ // ----- Set up our Vertex Array Object (VAO) to hold the shader attributes -----
+
+ // Create a VAO and bind to it
+ vaoId = glGenVertexArrays();
+ glBindVertexArray(vaoId);
+
+ // ----- Vertex Buffer Object (VBO) -----
+
+ // Create a VBO and bind to it
+ vboId = glGenBuffers();
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+
+ // Note: We do NOT copy the data into the buffer at this time - we do that on draw!
+
+ // Vertex attribute configuration
+ glVertexAttribPointer(shaderProgram.attribute("vertexLocation"), // Vertex location attribute index
+ VERTEX_COMPONENTS, // Number of components per vertex
+ GL_FLOAT, // Data type
+ false, // Normalised?
+ VERTEX_COMPONENTS * Float.BYTES, // Stride
+ 0); // Offset
+
+ // Unbind VBO
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // Enable the vertex attributes
+ glEnableVertexAttribArray(shaderProgram.attribute("vertexLocation"));
+
+ // Unbind VAO - all the buffer and attribute settings above will now be associated with our VAO
+ glBindVertexArray(0);
+ }
+
+ /**
+ * Default constructor.
+ *
+ * @param modelFilename The filename of the model to load.
+ * @param lineWidth The width of the lines used to draw the model in pixels.
+ */
+ // Note: width is along +/- x-axis, depth is along +/- z-axis, height is the location on
+ // the y-axis, numDivisions is how many lines to draw across each axis
+ public FabrikModel3D(String modelFilename, float lineWidth)
+ {
+ // Load the model, get the vertex data and put it into our vertex FloatBuffer
+ model = new Model(modelFilename);
+ modelData = model.getVertexFloatArray();
+ vertexFB = Utils.createFloatBuffer(model.getNumVertices() * VERTEX_COMPONENTS);
+
+ mLineWidth = lineWidth;
+
+ } // End of constructor
+
+ /** Private method to actually draw the model. */
+ private void drawModel(float lineWidth, Colour4f colour, Mat4f mvpMatrix)
+ {
+ // Enable our shader program and bind to our VAO
+ shaderProgram.use();
+ glBindVertexArray(vaoId);
+
+ // Bind to our VBO so we can update the axis data for this particular axis object
+ glBindBuffer(GL_ARRAY_BUFFER, vboId);
+
+ // Copy the data for this particular model into the vertex float buffer
+ // Note: The model is scaled to each individual bone length, hence the GL_DYNAMIC_DRAW performance hint.
+ vertexFB.put(modelData);
+ vertexFB.flip();
+ glBufferData(GL_ARRAY_BUFFER, vertexFB, GL_DYNAMIC_DRAW);
+
+ // Provide the mvp matrix uniform data
+ mvpMatrixFB.put( mvpMatrix.toArray() );
+ mvpMatrixFB.flip();
+ glUniformMatrix4fv(shaderProgram.uniform("mvpMatrix"), false, mvpMatrixFB);
+
+ // Provide the model vertex colour data
+ colourFB.put( colour.toArray() );
+ colourFB.flip();
+ glUniform4fv(shaderProgram.uniform("colour"), colourFB);
+
+ // Store the current GL_LINE_WIDTH
+ // IMPORTANT: We MUST allocate a minimum of 16 floats in our FloatBuffer in LWJGL, we CANNOT just get a FloatBuffer with 1 float!
+ // ALSO: glPushAttrib(GL_LINE_BIT); /* do stuff */ glPopAttrib(); should work instead of this in theory - but LWJGL fails with 'function not supported'.
+ glGetFloatv(GL_LINE_WIDTH, currentLineWidthFB);
+
+ /// Set the GL_LINE_WIDTH to be the width requested, as passed to the constructor
+ glLineWidth(lineWidth);
+
+ // Draw the model as lines
+ glDrawArrays( GL_LINES, 0, model.getNumVertices() );
+
+ // Reset the line width to the previous value
+ glLineWidth( currentLineWidthFB.get(0) );
+
+ // Unbind from our VBO
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // Unbind from our VAO
+ glBindVertexArray(0);
+
+ // Disable our shader program
+ shaderProgram.disable();
+ }
+
+ /**
+ * Draw a bone using the model loaded on this FabrikModel3D.
+ *
+ * @param bone The bone to draw.
+ * @param viewMatrix The view matrix, typically retrieved from the camera.
+ * @param projectionMatrix The projection matrix of our scene.
+ * @param colour The colour of the lines used to draw the bone.
+ */
+ public void drawBone(FabrikBone3D bone, Mat4f viewMatrix, Mat4f projectionMatrix, Colour4f colour)
+ {
+ // Clone the model and scale the clone to be twice as wide and deep, and scaled along the z-axis to match the bone length
+ Model modelCopy = Model.clone(model);
+ modelCopy.scale( 2.0f, 2.0f, bone.length() );
+
+ // Get our scaled model data
+ modelData = modelCopy.getVertexFloatArray();
+
+ // Construct a model matrix for this bone
+ Mat4f modelMatrix = new Mat4f( Mat3f.createRotationMatrix( bone.getDirectionUV().normalised() ), bone.getStartLocation() );
+
+ // Construct a ModelViewProjection and draw the model for this bone
+ Mat4f mvpMatrix = projectionMatrix.times(viewMatrix).times(modelMatrix);
+ this.drawModel(mLineWidth, colour, mvpMatrix);
+ }
+
+ /**
+ * Draw a bone using the model loaded on this FabrikModel3D using a default colour of white at full opacity.
+ *
+ * @param bone The bone to draw.
+ * @param viewMatrix The view matrix, typically retrieved from the camera.
+ * @param projectionMatrix The projection matrix of our scene.
+ */
+ public void drawBone(FabrikBone3D bone, Mat4f viewMatrix, Mat4f projectionMatrix)
+ {
+ this.drawBone(bone, viewMatrix, projectionMatrix, Utils.WHITE);
+ }
+
+ /**
+ * Draw a chain using the model loaded on this FabrikModel3D.
+ *
+ * @param chain The FabrikChain3D to draw the model as bones on.
+ * @param viewMatrix The view matrix, typically retrieved from the camera.
+ * @param projectionMatrix The projection matrix of our scene.
+ * @param colour The colour of the lines used to draw the model.
+ */
+ public void drawChain(FabrikChain3D chain, Mat4f viewMatrix, Mat4f projectionMatrix, Colour4f colour)
+ {
+ int numBones = chain.getNumBones();
+ for (int loop = 0; loop < numBones; ++loop)
+ {
+ this.drawBone( chain.getBone(loop), viewMatrix, projectionMatrix, colour );
+ }
+ }
+
+ /**
+ * Draw a chain using the model loaded on this FabrikModel3D using a default colour of white at full opacity.
+ *
+ * @param chain The FabrikChain3D to draw the model as bones on.
+ * @param viewMatrix The view matrix, typically retrieved from the camera.
+ * @param projectionMatrix The projection matrix of our scene.
+ */
+ public void drawChain(FabrikChain3D chain, Mat4f viewMatrix, Mat4f projectionMatrix)
+ {
+ int numBones = chain.getNumBones();
+ for (int loop = 0; loop < numBones; ++loop)
+ {
+ this.drawBone( chain.getBone(loop), viewMatrix, projectionMatrix, Utils.WHITE);
+ }
+ }
+
+ /**
+ * Draw a structure using the model loaded on this FabrikModel3D.
+ *
+ * @param structure The FabrikStructure3D to draw the model as bones on.
+ * @param viewMatrix The view matrix, typically retrieved from the camera.
+ * @param projectionMatrix The projection matrix of our scene.
+ * @param colour The colour of the lines used to draw the model.
+ */
+ public void drawStructure(FabrikStructure3D structure, Mat4f viewMatrix, Mat4f projectionMatrix, Colour4f colour)
+ {
+ int numChains = structure.getNumChains();
+ for (int loop = 0; loop < numChains; ++loop)
+ {
+ this.drawChain( structure.getChain(loop), viewMatrix, projectionMatrix, colour );
+ }
+ }
+
+ /**
+ * Draw a structure using the model loaded on this FabrikModel3D using a default colour of white at full opacity.
+ *
+ * @param structure The FabrikStructure3D to draw the model as bones on.
+ * @param viewMatrix The view matrix, typically retrieved from the camera.
+ * @param projectionMatrix The projection matrix of our scene.
+ */
+ public void drawStructure(FabrikStructure3D structure, Mat4f viewMatrix, Mat4f projectionMatrix)
+ {
+ int numChains = structure.getNumChains();
+ for (int loop = 0; loop < numChains; ++loop)
+ {
+ this.drawChain( structure.getChain(loop), viewMatrix, projectionMatrix, Utils.WHITE);
+ }
+ }
+
+ /**
+ * Line width property setter.
+ *
+ * Valid line widths are between 1.0f an 32.0f - values outside of this range will result
+ * in an IllegalArgumentException being thrown.
+ *
+ * @param lineWidth The width of the line used to draw this FabrikModel3D in pixels.
+ */
+ void setLineWidth(float lineWidth)
+ {
+ Utils.validateLineWidth(lineWidth);
+ mLineWidth = lineWidth;
+ }
+
+} // End of FabrikModel3D class
diff --git a/src/main/java/org/myrobotlab/caliko/GuiDemoStructure.java b/src/main/java/org/myrobotlab/caliko/GuiDemoStructure.java
new file mode 100644
index 0000000000..b1b7c6b824
--- /dev/null
+++ b/src/main/java/org/myrobotlab/caliko/GuiDemoStructure.java
@@ -0,0 +1,30 @@
+package org.myrobotlab.caliko;
+
+import org.myrobotlab.service.Caliko;
+
+import au.edu.federation.caliko.demo3d.CalikoDemoStructure3D;
+import au.edu.federation.utils.Mat4f;
+
+public class GuiDemoStructure extends CalikoDemoStructure3D {
+
+ private transient Caliko service;
+ protected String name;
+
+ public GuiDemoStructure(Caliko service, String name) {
+ this.service = service;
+ this.name = name;
+ }
+
+ @Override
+ public void setup() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void drawTarget(Mat4f mvpMatrix) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/src/main/java/org/myrobotlab/caliko/Model.java b/src/main/java/org/myrobotlab/caliko/Model.java
new file mode 100644
index 0000000000..887934bdc6
--- /dev/null
+++ b/src/main/java/org/myrobotlab/caliko/Model.java
@@ -0,0 +1,928 @@
+package org.myrobotlab.caliko;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import au.edu.federation.utils.Vec3f;
+import au.edu.federation.utils.Vec3i;
+
+//TODO: This is pretty ineficient - change all the for..each loops to be normals loops to stop Java allocating memory.
+//TODO: Also provide a proper copy-constructor rather than a clone method - they should do the same thing.
+
+/**
+ * A class to represent and load a 3D model in WaveFront .OBJ format.
+ *
+ * Vertices, normals, and faces with or without normal indices are supported.
+ *
+ * Models must be stored as triangles, not quads.
+ *
+ * There is no support for textures, texture coordinates, or grouped objects at this time.
+ *
+ * @author Al Lansley
+ * @version 0.5.1 - 07/01/2016
+ */
+public class Model
+{
+ private static boolean VERBOSE = false;
+ private static final String NUMBER_OF_VERTICES_LOG = "Number of vertices in data array: %d (%d bytes)";
+ private static final String NUMBER_OF_NORMALS_LOG = "Number of normals in data array: %d (%d bytes)";
+ private static final String WRONG_COMPONENT_COUNT_LOG = "Found %s data with wrong component count at line number: %d - Skipping!";
+
+ // ---------- Private Properties ----------
+
+ // These are the models values as read from the file - they are not the final, consolidated model data
+ private List vertices = new ArrayList<>();
+ private List normals = new ArrayList<>();
+ private List normalIndices = new ArrayList<>();
+ private List faces = new ArrayList<>();
+ //ArrayList texCoords = new ArrayList();
+
+ // The vertexData and normalData arrays are the final consolidated data which we can draw with.
+ //
+ // Note: If the model has only vertices and/or normals, then the vertexData and normalData will be a direct 'unwrapped'
+ // version of the vertices and normals arrays. However, if we're using faces then there will likely be a lower number of
+ // vertices / normals as each vertex / normal may be used more than once in the model - the end result of this is that
+ // the vertexData and normalData will likely be larger than the 'as-read-from-file' vertices and normals arrays because
+ // of this duplication of values from the face data.
+ private List vertexData = new ArrayList<>();
+ private List normalData = new ArrayList<>();
+ //ArrayList texCoordData = new ArrayList();
+
+ // Counters to keep track of how many vertices, normals, normal indices, texture coordinates and faces
+ private int numVertices;
+ private int numNormals;
+ private int numNormalIndices;
+ private int numFaces;
+ //private int numTexCoords;
+
+ // ---------- Public Methods ----------
+
+ /** Default constructor. */
+ public Model() { }
+
+ /**
+ * Constructor which creates a model object and loads the model from file.
+ *
+ * @param filename The file to load the model data from.
+ */
+ public Model(String filename) { load(filename); }
+
+ /** Enable verbose messages. */
+ public static void enableVerbose() { VERBOSE = true; }
+
+ /** Disable verbose messages. */
+ public static void disableVerbose() { VERBOSE = false; }
+
+ // Method provide create a deep copy of a Model so we can say copyOfMyModel.equals(myModel);
+ // Note: We only deep copy the vertexData and normalData arrays, not all the vectors!
+ public static Model clone(Model sourceModel)
+ {
+ // Create a new Model which we will clone across the data to from our source model
+ Model model = new Model();
+
+ // Update the counts of vertices and normals for the clone to match the source model
+ model.numVertices = sourceModel.getNumVertices();
+ model.numNormals = sourceModel.getNumNormals();
+
+ // If the source model has vertices then copy them across to the clone...
+ if (model.numVertices > 0)
+ {
+ // For (foo IN bar) loops leak memory - go old-school
+ int vertCount = sourceModel.getNumVertices();
+ for (int loop = 0; loop < vertCount; loop++)
+ {
+ //model.vertexData.add(f)
+ }
+ for ( Float f : sourceModel.getVertexData() ) {
+ model.vertexData.add(f);
+ }
+ }
+ else // ...or abort if we have no vertices to copy!
+ {
+ throw new RuntimeException("Model created using clone method has 0 vertices!");
+ }
+
+ // If the source model has normals then copy them across to the clone...
+ if (model.numNormals > 0)
+ {
+ for ( Float f : sourceModel.getNormalData() ) {
+ model.normalData.add(f);
+ }
+ }
+ else // ...or (potentially) inform the user if they are no normals. This is not necessarily a deal breaker though, so we don't abort.
+ {
+ if (VERBOSE) {
+ System.out.println( "Model created using clone method has 0 normals - continuing...");
+ }
+ }
+
+ // Display final status if appropriate
+ if (VERBOSE) {
+ System.out.println( "Model successfully cloned.");
+ }
+
+ // Finally, return our cloned model
+ return model;
+ }
+
+ /**
+ * Load a .OBJ model from file.
+ *
+ * By default, no feedback is provided on the model loading. If you wish to see what's going on
+ * internally, call Model.enableVerbose() before loading the model - this will display statistics
+ * about any vertices/normals/normal indices/faces found in the model, as well as any malformed data.
+ *
+ * If the model file does not contain any vertices then a RuntimeException is thrown.
+ * If the file cannot be found then a FileNotFoundException is thrown.
+ * If there was a file-system-type error when reading the file then an IOException is thrown.
+ *
+ * @param filename The name of the Wavefront .OBJ format model to load, include the path if necessary.
+ * @return Whether the file loaded successfully or not. Loading with warnings still counts as a
+ * successful load - if necessary enable verbose mode to ensure your model loaded cleanly.
+ */
+ public boolean load(String filename)
+ {
+ // Load the model file
+ boolean modelLoadedCleanly = loadModel(filename);
+
+ // Did we load the file without errors?
+ if (VERBOSE)
+ {
+ if (modelLoadedCleanly) {
+ System.out.println("Model loaded cleanly.");
+ }
+ else {
+ System.out.println("Model loaded with errors.");
+ }
+ }
+
+ // Do we have vertices? If not then this is a non-recoverable error and we abort!
+ if ( hasVertices() )
+ {
+ if (VERBOSE)
+ {
+ System.out.println("Model vertex count: " + getNumVertices() );
+ if ( hasFaces() ) {
+ System.out.println( "Model face count: " + getNumFaces() );
+ }
+ if ( hasNormals() ) {
+ System.out.println( "Model normal count: " + getNumNormals() );
+ }
+ if ( hasNormalIndices() ) {
+ System.out.println( "Model normal index count: " + getNumNormalIndices() );
+ }
+ }
+ }
+ else { throw new RuntimeException("Model has no vertices."); }
+
+ // Transfer the loaded data in our vectors to the data arrays
+ setupData();
+
+ // Delete the vertices, normals, normalIndices and faces Lists as we now have the final
+ // data stored in the vertexData and normalData Lists.
+ vertices.clear();
+ normals.clear();
+ normalIndices.clear();
+ faces.clear();
+ vertices = null;
+ normals = null;
+ normalIndices = null;
+ faces = null;
+
+ // Indicate that the model loaded successfully
+ return true;
+ }
+
+
+ // ---------- Getters ----------
+
+ /**
+ * Get the vertex data as a list of floats.
+ *
+ * @return The vertex data.
+ */
+ public List getVertexData() { return vertexData; }
+
+ /**
+ * Get the vertex normals as a list of floats.
+ *
+ * @return The vertex normal data.
+ */
+ public List getNormalData() { return normalData; }
+
+ /**
+ * Get the vertex data as a float array suitable for transfer into a FloatBuffer for drawing.
+ *
+ * @return The vertex data as a float array.
+ **/
+ public float[] getVertexFloatArray()
+ {
+ // How many floats are there in our list of vertex data?
+ int numVertexFloats = vertexData.size();
+
+ // Create an array big enough to hold them
+ float[] vertexFloatArray = new float[numVertexFloats];
+
+ // Loop over each item in the list, setting it to the appropriate element in the array
+ for (int loop = 0; loop < numVertexFloats; loop++) {
+ vertexFloatArray[loop] = vertexData.get(loop);
+ }
+
+ // Finally, return the float array
+ return vertexFloatArray;
+ }
+
+ /**
+ * Get the vertex normal data as a float array suitable for transfer into a FloatBuffer for drawing.
+ *
+ * @return The vertex normal data as a float array.
+ */
+ public float[] getNormalFloatArray()
+ {
+ // How many floats are there in our list of normal data?
+ int numNormalFloats = normalData.size();
+
+ // Create an array big enough to hold them
+ float[] normalFloatArray = new float[numNormalFloats];
+
+ // Loop over each item in the list, setting it to the appropriate element in the array
+ for (int loop = 0; loop < numNormalFloats; loop++) {
+ normalFloatArray[loop] = normalData.get(loop);
+ }
+
+ // Finally, return the float array
+ return normalFloatArray;
+ }
+
+ // Methods to get the sizes of various data arrays
+ // Note: Type.BYTES returns the size of on object of this type in Bytes, and we multiply
+ // by 3 because there are 3 components to a vertex (x/y/z), normal (s/t/p) and 3 vertexes comprising a face (i.e. triangle)
+
+ /**
+ * Get the vertex data size in bytes.
+ *
+ * @return The vertex data size in bytes.
+ */
+ public int getVertexDataSizeBytes() { return numVertices * 3 * Float.BYTES; }
+
+ /**
+ * Get the vertex normal data size in bytes.
+ *
+ * @return The vertex normal data size in bytes.
+ */
+ public int getNormalDataSizeBytes() { return numNormals * 3 * Float.BYTES; }
+
+ /**
+ * Get the face data size in bytes.
+ *
+ * @return The face data size in bytes.
+ **/
+ public int getFaceDataSizeBytes() { return numFaces * 3 * Integer.BYTES; }
+
+ /**
+ * Get the number of vertices in this model.
+ *
+ * @return The number of vertices in this model.
+ */
+ public int getNumVertices() { return numVertices; }
+
+ /**
+ * Get the number of vertex normals in this model.
+ *
+ * @return The number of normals in this model.
+ */
+ public int getNumNormals() { return numNormals; }
+
+ /** Get the number of normal indices in this model.
+ *
+ *
+ * @return The number of normal indices in this model.
+ */
+ public int getNumNormalIndices() { return numNormalIndices; }
+
+ /**
+ * Get the number of faces in this model.
+ *
+ * @return The number of faces in this model.
+ */
+ public int getNumFaces() { return numFaces; }
+
+ // ---------- Utility Methods ----------
+
+ /**
+ * Scale this model uniformly along the x/y/z axes.
+ *
+ * @param scale The amount to scale the model.
+ **/
+ public void scale(float scale)
+ {
+ int numVerts = vertexData.size();
+ for (int loop = 0; loop < numVerts; ++loop) {
+ vertexData.set(loop, vertexData.get(loop) * scale);
+ }
+ }
+
+ /**
+ * Scale this model on the X axis.
+ *
+ * @param scale The amount to scale the model on the X axis.
+ */
+ public void scaleX(float scale)
+ {
+ int numVerts = vertexData.size();
+ for (int loop = 0; loop < numVerts; loop += 3) {
+ vertexData.set( loop, vertexData.get(loop) * scale);
+ }
+ }
+
+ /**
+ * Scale this model on the Y axis.
+ *
+ * @param scale The amount to scale the model on the Y axis.
+ */
+ public void scaleY(float scale)
+ {
+ int numVerts = vertexData.size();
+ for (int loop = 1; loop < numVerts; loop += 3) {
+ vertexData.set( loop, vertexData.get(loop) * scale);
+ }
+ }
+
+ /**
+ * Scale this model on the Z axis.
+ *
+ * @param scale The amount to scale the model on the Z axis.
+ */
+ public void scaleZ(float scale)
+ {
+ int numVerts = vertexData.size();
+ for (int loop = 2; loop < numVerts; loop += 3) {
+ vertexData.set( loop, vertexData.get(loop) * scale);
+ }
+ }
+
+ /**
+ * Scale this model by various amounts along separate axes.
+ *
+ * @param xScale The amount to scale the model on the X axis.
+ * @param yScale The amount to scale the model on the Y axis.
+ * @param zScale The amount to scale the model on the Z axis.
+ */
+ public void scale(float xScale, float yScale, float zScale)
+ {
+ int numVerts = vertexData.size();
+ for (int loop = 0; loop < numVerts; ++loop)
+ {
+ switch (loop % 3)
+ {
+ case 0:
+ vertexData.set(loop, vertexData.get(loop) * xScale);
+ break;
+ case 1:
+ vertexData.set(loop, vertexData.get(loop) * yScale);
+ break;
+ case 2:
+ vertexData.set(loop, vertexData.get(loop) * zScale);
+ break;
+ }
+ }
+ }
+
+ /** Print out the vertices of this model. */
+ public void printVertices() {
+ for (Vec3f v : vertices) {
+ System.out.println( "Vertex: " + v.toString() );
+ }
+ }
+
+ /**
+ * Print out the vertex normal data for this model.
+ *
+ * Note: This is the contents of the normals list, not the (possibly expanded) normalData array.
+ */
+ public void printNormals()
+ {
+ for (Vec3f n : normals) {
+ System.out.println( "Normal: " + n.toString() );
+ }
+ }
+
+ /**
+ * Print the face data of this model.
+ *
+ * Note: Faces are ONE indexed, not zero indexed.
+ */
+ public void printFaces()
+ {
+ for (Vec3i face : faces) {
+ System.out.println( "Face: " + face.toString() );
+ }
+ }
+
+ /**
+ * Print the vertex data of this model.
+ *
+ * Note: This is the contents of the vertexlData array which is actually used when drawing - and which may be
+ * different to the 'vertices' list when using faces (where vertices get re-used).
+ */
+ public void printVertexData()
+ {
+ for (int loop = 0; loop < vertexData.size(); loop += 3)
+ {
+ System.out.println( "Vertex data element " + (loop / 3) + " is x: " + vertexData.get(loop) + "\ty: " + vertexData.get(loop+1) + "\tz: " + vertexData.get(loop+2) );
+ }
+ }
+
+ /**
+ * Print the normal data of this model.
+ *
+ * Note: This is the contents of the normalData array which is actually used when drawing - and which may be
+ * different to the 'normals' list when using faces (where normals get re-used).
+ */
+ public void printNormalData()
+ {
+ for (int loop = 0; loop < normalData.size(); loop += 3)
+ {
+ System.out.println( "Normal data element " + (loop / 3) + " is x: " + normalData.get(loop) + "\ty: " + normalData.get(loop+1) + "\tz: " + normalData.get(loop+2));
+ }
+ }
+
+ // ---------- Private Methods ----------
+
+ // Method to read through the model file adding all vertices, faces and normals to our
+ // vertices, faces and normals vectors.
+
+ // Note: This does NOT transfer the data into our vertexData, faceData or normalData arrays!
+ // That must be done as a separate step by calling setupData() after building up the
+ // arraylists with this method!
+
+ // Also: This method does not decrement the face number of normal index by 1 (because .OBJ
+ // files start their counts at 1) to put them in a range starting from 0, that job
+ // is done in the setupData() method performed after calling this method!
+ private boolean loadModel(String filename)
+ {
+ // Initialise lists
+ vertices = new ArrayList<>();
+ normals = new ArrayList<>();
+ normalIndices = new ArrayList<>();
+ faces = new ArrayList<>();
+ //texCoords = new ArrayList();
+ normalData = new ArrayList<>();
+ //texCoordData = new ArrayList();
+
+ // Our vectors of attributes are initially empty
+ numVertices = 0;
+ numNormals = 0;
+ numNormalIndices = 0;
+ numFaces = 0;
+ //numTexCoords = 0;
+
+ boolean loadedCleanly = true;
+
+ // Counter to keep track of what line we're on
+ int lineCount = 0;
+
+ // Use this for jar packaged resources
+ InputStream is = this.getClass().getResourceAsStream(filename);
+ try (BufferedReader br = new BufferedReader( new InputStreamReader(is) ) ) // This version loads from within jar archive, required for caliko-demo-jar-with-resources.jar
+ //try (BufferedReader br = new BufferedReader( new FileReader(filename) ) ) // Use this for loading from file in Eclipse or such
+ {
+ // We'll read through the file one line at a time - this will hold the current line we're working on
+ String line;
+
+ // While there are lines left to read in the file...
+ while ((line = br.readLine()) != null)
+ {
+ ++lineCount;
+
+ // If the line isn't empty (the 1 character is the carriage return), process it...
+ if (line.length() > 1)
+ {
+ // Split line on spaces into an array of strings.The + on the end of "\\s+" means 'do not accept
+ // blank entries' which can occur if you have two consecutive space characters (as happens when
+ // you export a .obj from 3ds max - you get "v 1.23 4.56 7.89" etc.)
+ String[] token = line.split("\\s+");
+
+ // If the first token is "v", then we're dealing with vertex data
+ if ( "v".equalsIgnoreCase(token[0]) )
+ {
+ // As long as there's 4 tokens on the line...
+ if (token.length == 4)
+ {
+ // ...get the remaining 3 tokens as the x/y/z float values...
+ float x = Float.parseFloat(token[1]);
+ float y = Float.parseFloat(token[2]);
+ float z = Float.parseFloat(token[3]);
+
+ // ... and push them into the vertices vector ...
+ vertices.add( new Vec3f(x, y, z) );
+
+ // .. then increase our vertex count.
+ numVertices++;
+ }
+ else // If we got vertex data without 3 components - whine!
+ {
+ loadedCleanly = false;
+ System.out.printf(WRONG_COMPONENT_COUNT_LOG,"vertex",lineCount);
+ }
+
+ }
+ else if ( "vn".equalsIgnoreCase(token[0]) ) // If the first token is "vn", then we're dealing with a vertex normal
+ {
+ // As long as there's 4 tokens on the line...
+ if (token.length == 4)
+ {
+ // ...get the remaining 3 tokens as the x/y/z normal float values...
+ float normalX = Float.parseFloat(token[1]);
+ float normalY = Float.parseFloat(token[2]);
+ float normalZ = Float.parseFloat(token[3]);
+
+ // ... and push them into the normals vector ...
+ normals.add( new Vec3f(normalX, normalY, normalZ) );
+
+ // .. then increase our normal count.
+ numNormals++;
+ }
+ else // If we got normal data without 3 components - whine!
+ {
+ loadedCleanly = false;
+ System.out.printf(WRONG_COMPONENT_COUNT_LOG,"normal",lineCount);
+ }
+
+ } // End of vertex line parsing
+
+ // If the first token is "f", then we're dealing with faces
+ //
+ // Note: Faces can be described in two ways - we can have data like 'f 123 456 789' which means that the face is comprised
+ // of vertex 123, vertex 456 and vertex 789. Or, we have have data like f 123//111 456//222 789//333 which means that
+ // the face is comprised of vertex 123 using normal 111, vertex 456 using normal 222 and vertex 789 using normal 333.
+ else if ( "f".equalsIgnoreCase(token[0]) )
+ {
+ // Check if there's a double-slash in the line
+ int pos = line.indexOf("//");
+
+ // As long as there's four tokens on the line and they don't contain a "//"...
+ if ( (token.length == 4) && (pos == -1) )
+ {
+ // ...get the face vertex numbers as ints ...
+ int v1 = Integer.parseInt(token[1]);
+ int v2 = Integer.parseInt(token[2]);
+ int v3 = Integer.parseInt(token[3]);
+
+ // ... and push them into the faces vector ...
+ faces.add( new Vec3i(v1, v2, v3) );
+
+ // .. then increase our face count.
+ numFaces++;
+ }
+ else if ( (token.length == 4) && (pos != -1) ) // 4 tokens and found 'vertex//normal' notation?
+ {
+ // ----- Get the 1st of three tokens as a String -----
+
+ // Find where the double-slash starts in that token
+ int faceEndPos = token[1].indexOf("//");
+
+ // Put sub-String from the start to the beginning of the double-slash into our subToken String
+ String faceToken1 = token[1].substring(0, faceEndPos);
+
+ // Convert face token value to int
+ int ft1 = Integer.parseInt(faceToken1);
+
+ // Mark the start of our next subtoken
+ int nextTokenStartPos = faceEndPos + 2;
+
+ // Copy from first character after the "//" to the end of the token
+ String normalToken1 = token[1].substring(nextTokenStartPos);
+
+ // Convert normal token value to int
+ int nt1 = Integer.parseInt(normalToken1);
+
+ // ----- Get the 2nd of three tokens as a String -----
+
+ // Find where the double-slash starts in that token
+ faceEndPos = token[2].indexOf("//");
+
+ // Put sub-String from the start to the beginning of the double-slash into our subToken String
+ String faceToken2 = token[2].substring(0, faceEndPos);
+
+ // Convert face token value to int
+ int ft2 = Integer.parseInt(faceToken2);
+
+ // Mark the start of our next subtoken
+ nextTokenStartPos = faceEndPos + 2;
+
+ // Copy from first character after the "//" to the end of the token
+ String normalToken2 = token[2].substring(nextTokenStartPos);
+
+ // Convert normal token value to int
+ int nt2 = Integer.parseInt(normalToken2);
+
+ // ----- Get the 3rd of three tokens as a String -----
+
+ // Find where the double-slash starts in that token
+ faceEndPos = token[3].indexOf("//");
+
+ // Put sub-String from the start to the beginning of the double-slash into our subToken String
+ String faceToken3 = token[3].substring(0, faceEndPos);
+
+ // Convert face token value to int
+ int ft3 = Integer.parseInt(faceToken3);
+
+ // Mark the start of our next subtoken
+ nextTokenStartPos = faceEndPos + 2;
+
+ // Copy from first character after the "//" to the end
+ String normalToken3 = token[3].substring(nextTokenStartPos);
+
+ // Convert normal token value to int
+ int nt3 = Integer.parseInt(normalToken3);
+
+
+ // Finally, add the face to the faces array list and increment the face count...
+ faces.add( new Vec3i(ft1, ft2, ft3) );
+ numFaces++;
+
+ // ...and do the same for the normal indices and the normal index count.
+ normalIndices.add( new Vec3i(nt1, nt2, nt3) );
+ numNormalIndices++;
+
+ }
+ else // If we got face data without 3 components - whine!
+ {
+ loadedCleanly = false;
+ System.out.printf(WRONG_COMPONENT_COUNT_LOG,"face",lineCount);
+ }
+
+ } // End of if token is "f" (i.e. face indices)
+
+ // IMPLIED ELSE: If first token is something we don't recognise then we ignore it as a comment.
+
+ } // End of line parsing section
+
+ } // End of if line length > 1 check
+
+ // No need to close the file ( i.e. br.close() ) - try-with-resources does that for us.
+ }
+ catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); }
+ catch (IOException ioe) { ioe.printStackTrace(); }
+
+ // Return our boolean flag to say whether we loaded the model cleanly or not
+ return loadedCleanly;
+ }
+
+ // ----- Helper Methods -----
+
+ /**
+ * Return whether or not this model contains vertex data.
+ *
+ * @return whether or not this model contains vertex data.
+ */
+ public boolean hasVertices() { return (numVertices > 0); }
+
+ /**
+ * Return whether or not this model contains face data.
+ *
+ * @return whether or not this model contains face data.
+ */
+ public boolean hasFaces() { return (numFaces > 0); }
+
+ /**
+ * Return whether or not this model contains normal data.
+ *
+ * @return whether or not this model contains normal data.
+ */
+ public boolean hasNormals() { return (numNormals > 0); }
+
+ /**
+ * Return whether or not this model contains normal index data.
+ *
+ * @return whether or not this model contains normal index data.
+ */
+ public boolean hasNormalIndices() { return (numNormalIndices > 0); }
+
+ /**
+ * Set up our plain arrays of floats for OpenGL to work with.
+ *
+ * Note: We CANNOT have size mismatches! The vertex count must match the
+ * normal count i.e. every vertex must have precisely ONE normal - no more, no less!
+ */
+ private void setupData()
+ {
+ if (VERBOSE) {
+ System.out.println( "Setting up model data to draw as arrays.");
+ }
+
+ // If we ONLY have vertex data, then transfer just that...
+ if ( ( hasVertices() ) && ( !hasFaces() ) && ( !hasNormals() ) )
+ {
+ if (VERBOSE) {
+ System.out.println( "Model has no faces or normals. Transferring vertex data only.");
+ }
+
+ // Reset the vertex count
+ numVertices = 0;
+
+ // Transfer all vertices from the vertices vector to the vertexData array
+ for (Vec3f v : vertices)
+ {
+ vertexData.add( v.x );
+ vertexData.add( v.y );
+ vertexData.add( v.z );
+ ++numVertices;
+ }
+
+ // Print a summary of the vertex data
+ if (VERBOSE) {
+ System.out.printf( NUMBER_OF_VERTICES_LOG, numVertices, getVertexDataSizeBytes());
+ }
+ }
+ // If we have vertices AND faces BUT NOT normals...
+ else if ( ( hasVertices() ) && ( hasFaces() ) && ( !hasNormals() ) )
+ {
+ if (VERBOSE) {
+ System.out.println("Model has vertices and faces, but no normals. Per-face normals will be generated.") ;
+ }
+
+ // Create the vertexData and normalData arrays from the vector of faces
+ // Note: We generate the face normals ourselves.
+ int vertexCount = 0;
+ int normalCount = 0;
+
+ for (Vec3i iv : faces)
+ {
+ // Get the numbers of the three vertices that this face is comprised of
+ int firstVertexNum = iv.x;
+ int secondVertexNum = iv.y;
+ int thirdVertexNum = iv.z;
+
+ // Now that we have the vertex numbers, we need to get the actual vertices
+ // Note: We subtract 1 from the number of the vertex because faces start at
+ // face number 1 in the .OBJ format, while in our code the first vertex
+ // will be at location zero.
+ Vec3f faceVert1 = vertices.get(firstVertexNum - 1);
+ Vec3f faceVert2 = vertices.get(secondVertexNum - 1);
+ Vec3f faceVert3 = vertices.get(thirdVertexNum - 1);
+
+ // Now that we have the 3 vertices, we need to calculate the normal of the face
+ // formed by these vertices...
+
+ // Convert this vertex data into a pure form
+ Vec3f v1 = faceVert2.minus(faceVert1);
+ Vec3f v2 = faceVert3.minus(faceVert1);
+
+ // Generate the normal as the cross product and normalise it
+ Vec3f normal = v1.cross(v2);
+ Vec3f normalisedNormal = normal.normalise();
+
+ // Put the vertex data into our vertexData array
+ vertexData.add( faceVert1.x );
+ vertexData.add( faceVert1.y );
+ vertexData.add( faceVert1.z );
+ vertexCount++;
+
+ vertexData.add( faceVert2.x );
+ vertexData.add( faceVert2.y );
+ vertexData.add( faceVert2.z );
+ vertexCount++;
+
+ vertexData.add( faceVert3.x );
+ vertexData.add( faceVert3.y );
+ vertexData.add( faceVert3.z );
+ vertexCount++;
+
+ // Put the normal data into our normalData array
+ //
+ // Note: we put the same normal into the normalData array for each of the 3 vertices comprising the face!
+ // This gives use a 'faceted' looking model, but is easy! You could try to calculate an interpolated
+ // normal based on surrounding normals, but that's not a trivial task (although 3ds max will do it for you
+ // if you load up the model and export it with normals!)
+ normalData.add( normalisedNormal.x );
+ normalData.add( normalisedNormal.y );
+ normalData.add( normalisedNormal.z );
+ normalCount++;
+
+ normalData.add( normalisedNormal.x );
+ normalData.add( normalisedNormal.y );
+ normalData.add( normalisedNormal.z );
+ normalCount++;
+
+ normalData.add( normalisedNormal.x );
+ normalData.add( normalisedNormal.y );
+ normalData.add( normalisedNormal.z );
+ normalCount++;
+
+ } // End of loop iterating over the model faces
+
+ numVertices = vertexCount;
+ numNormals = normalCount;
+
+ if (VERBOSE)
+ {
+ System.out.printf( NUMBER_OF_VERTICES_LOG, numVertices, getVertexDataSizeBytes());
+ System.out.printf( NUMBER_OF_NORMALS_LOG, numNormals, getNormalDataSizeBytes());
+ }
+ }
+ // If we have vertices AND faces AND normals AND normalIndices...
+ else if ( ( hasVertices() ) && ( hasFaces() ) && ( hasNormals() ) && ( hasNormalIndices() ) )
+ {
+ if (VERBOSE)
+ {
+ System.out.println("Model has vertices, faces, normals & normal indices. Transferring data.");
+ }
+
+ //FIXME: Change this to use the numVertices and numNormals directly - I don't see a reason to use separate vertexCount and normalCount vars...
+
+ int vertexCount = 0;
+ int normalCount = 0;
+
+ // Look up each vertex specified by each face and add the vertex data to the vertexData array
+ for (Vec3i iv : faces)
+ {
+ // Get the numbers of the three vertices that this face is comprised of
+ int firstVertexNum = iv.x;
+ int secondVertexNum = iv.y;
+ int thirdVertexNum = iv.z;
+
+ // Now that we have the vertex numbers, we need to get the actual vertices
+ // Note: We subtract 1 from the number of the vertex because faces start at
+ // face number 1 in the .oBJ format, while in our code the first vertex
+ // will be at location zero.
+ Vec3f faceVert1 = vertices.get(firstVertexNum - 1);
+ Vec3f faceVert2 = vertices.get(secondVertexNum - 1);
+ Vec3f faceVert3 = vertices.get(thirdVertexNum - 1);
+
+ // Put the vertex data into our vertexData array
+ vertexData.add( faceVert1.x );
+ vertexData.add( faceVert1.y );
+ vertexData.add( faceVert1.z );
+ ++vertexCount;
+
+ vertexData.add( faceVert2.x );
+ vertexData.add( faceVert2.y );
+ vertexData.add( faceVert2.z );
+ ++vertexCount;
+
+ vertexData.add( faceVert3.x );
+ vertexData.add( faceVert3.y );
+ vertexData.add( faceVert3.z );
+ ++vertexCount;
+ }
+
+ // Look up each normal specified by each normal index and add the normal data to the normalData array
+ for (Vec3i normInd : normalIndices)
+ {
+ // Get the numbers of the three normals that this face uses
+ int firstNormalNum = normInd.x;
+ int secondNormalNum = normInd.y;
+ int thirdNormalNum = normInd.z;
+
+ // Now that we have the normal index numbers, we need to get the actual normals
+ // Note: We subtract 1 from the number of the normal because normals start at
+ // number 1 in the .obJ format, while in our code the first vertex
+ // will be at location zero.
+ Vec3f normal1 = normals.get(firstNormalNum - 1);
+ Vec3f normal2 = normals.get(secondNormalNum - 1);
+ Vec3f normal3 = normals.get(thirdNormalNum - 1);
+
+ // Put the normal data into our normalData array
+ normalData.add( normal1.x );
+ normalData.add( normal1.y );
+ normalData.add( normal1.z );
+ normalCount++;
+
+ normalData.add( normal2.x );
+ normalData.add( normal2.y );
+ normalData.add( normal2.z );
+ normalCount++;
+
+ normalData.add( normal3.x );
+ normalData.add( normal3.y );
+ normalData.add( normal3.z );
+ normalCount++;
+
+ } // End of loop iterating over the model faces
+
+ numVertices = vertexCount;
+ numNormals = normalCount;
+
+ if (VERBOSE)
+ {
+ System.out.printf( NUMBER_OF_VERTICES_LOG, numVertices, getVertexDataSizeBytes());
+ System.out.printf( NUMBER_OF_NORMALS_LOG, numNormals, getNormalDataSizeBytes());
+ }
+ }
+ else
+ {
+ System.out.println("Something bad happened in Model.setupData() =(");
+ }
+
+ } // End of setupData method
+
+} // End of Model class
diff --git a/src/main/java/org/myrobotlab/caliko/OpenGLWindow.java b/src/main/java/org/myrobotlab/caliko/OpenGLWindow.java
new file mode 100644
index 0000000000..9582c6adf7
--- /dev/null
+++ b/src/main/java/org/myrobotlab/caliko/OpenGLWindow.java
@@ -0,0 +1,505 @@
+package org.myrobotlab.caliko;
+
+import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MAJOR;
+import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR;
+import static org.lwjgl.glfw.GLFW.GLFW_CURSOR;
+import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED;
+import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_NORMAL;
+import static org.lwjgl.glfw.GLFW.GLFW_FOCUSED;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_A;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_C;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_D;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_DOWN;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_ESCAPE;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_F;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_L;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_M;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_P;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_R;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_S;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_SPACE;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_UP;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_W;
+import static org.lwjgl.glfw.GLFW.GLFW_KEY_X;
+import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_1;
+import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE;
+import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE;
+import static org.lwjgl.glfw.GLFW.GLFW_PRESS;
+import static org.lwjgl.glfw.GLFW.GLFW_RELEASE;
+import static org.lwjgl.glfw.GLFW.GLFW_REPEAT;
+import static org.lwjgl.glfw.GLFW.GLFW_RESIZABLE;
+import static org.lwjgl.glfw.GLFW.GLFW_SAMPLES;
+import static org.lwjgl.glfw.GLFW.GLFW_TRUE;
+import static org.lwjgl.glfw.GLFW.GLFW_VISIBLE;
+import static org.lwjgl.glfw.GLFW.glfwCreateWindow;
+import static org.lwjgl.glfw.GLFW.glfwDestroyWindow;
+import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor;
+import static org.lwjgl.glfw.GLFW.glfwGetVideoMode;
+import static org.lwjgl.glfw.GLFW.glfwInit;
+import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent;
+import static org.lwjgl.glfw.GLFW.glfwSetCursorPos;
+import static org.lwjgl.glfw.GLFW.glfwSetCursorPosCallback;
+import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback;
+import static org.lwjgl.glfw.GLFW.glfwSetInputMode;
+import static org.lwjgl.glfw.GLFW.glfwSetKeyCallback;
+import static org.lwjgl.glfw.GLFW.glfwSetMouseButtonCallback;
+import static org.lwjgl.glfw.GLFW.glfwSetWindowPos;
+import static org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose;
+import static org.lwjgl.glfw.GLFW.glfwSetWindowSizeCallback;
+import static org.lwjgl.glfw.GLFW.glfwSetWindowTitle;
+import static org.lwjgl.glfw.GLFW.glfwShowWindow;
+import static org.lwjgl.glfw.GLFW.glfwSwapBuffers;
+import static org.lwjgl.glfw.GLFW.glfwSwapInterval;
+import static org.lwjgl.glfw.GLFW.glfwTerminate;
+import static org.lwjgl.glfw.GLFW.glfwWindowHint;
+import static org.lwjgl.opengl.GL11.GL_BLEND;
+import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
+import static org.lwjgl.opengl.GL11.GL_LEQUAL;
+import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
+import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
+import static org.lwjgl.opengl.GL11.glBlendFunc;
+import static org.lwjgl.opengl.GL11.glClearColor;
+import static org.lwjgl.opengl.GL11.glClearDepth;
+import static org.lwjgl.opengl.GL11.glDepthFunc;
+import static org.lwjgl.opengl.GL11.glEnable;
+import static org.lwjgl.opengl.GL11.glViewport;
+import static org.lwjgl.system.MemoryUtil.NULL;
+
+import org.lwjgl.glfw.GLFWCursorPosCallback;
+import org.lwjgl.glfw.GLFWErrorCallback;
+import org.lwjgl.glfw.GLFWKeyCallback;
+import org.lwjgl.glfw.GLFWMouseButtonCallback;
+import org.lwjgl.glfw.GLFWVidMode;
+import org.lwjgl.glfw.GLFWWindowSizeCallback;
+import org.lwjgl.opengl.GL;
+import org.myrobotlab.service.Caliko;
+import org.myrobotlab.service.config.CalikoConfig;
+
+import au.edu.federation.caliko.visualisation.Axis;
+import au.edu.federation.caliko.visualisation.Camera;
+import au.edu.federation.caliko.visualisation.Grid;
+//import au.edu.federation.caliko.demo2d.CalikoDemoStructure2DFactory.CalikoDemoStructure2DEnum;
+//import au.edu.federation.caliko.demo3d.CalikoDemoStructure3DFactory.CalikoDemoStructure3DEnum;
+import au.edu.federation.utils.Mat4f;
+import au.edu.federation.utils.Utils;
+import au.edu.federation.utils.Vec2f;
+import au.edu.federation.utils.Vec3f;
+
+/**
+ * Class to set up an OpenGL window.
+ *
+ * @author Al Lansley
+ * @version 0.3 - 07/12/2015
+ */
+public class OpenGLWindow
+{
+
+ // Mouse cursor locations in screen-space and world-space
+ public Vec2f screenSpaceMousePos = null;
+ public Vec2f worldSpaceMousePos = new Vec2f();
+
+ // Window properties
+ long mWindowId;
+ int mWindowWidth;
+ int mWindowHeight;
+ float mAspectRatio;
+
+ // Matrices
+ Mat4f mProjectionMatrix;
+ Mat4f mModelMatrix = new Mat4f(1.0f);
+ Mat4f mMvpMatrix = new Mat4f();
+
+ // Matrix properties
+ boolean mOrthographicProjection; // Use orthographic projection? If false, we use a standard perspective projection
+ float mVertFoVDegs;
+ float mZNear;
+ float mZFar;
+ float mOrthoExtent;
+
+ // We need to strongly reference callback instances so that they don't get garbage collected.
+ private GLFWErrorCallback errorCallback;
+ private GLFWKeyCallback keyCallback;
+ private GLFWWindowSizeCallback windowSizeCallback;
+ private GLFWMouseButtonCallback mouseButtonCallback;
+ private GLFWCursorPosCallback cursorPosCallback;
+
+ CalikoConfig config = null;
+
+ Caliko service = null;
+ public Application application;
+
+ // Constructor
+ public OpenGLWindow(Application application, Caliko service, int windowWidth, int windowHeight, float vertFoVDegs, float zNear, float zFar, float orthoExtent)
+ {
+ this.service = service;
+ this.application = application;
+
+ // Set properties and create the projection matrix
+ mWindowWidth = windowWidth <= 0 ? 1 : windowWidth;
+ mWindowHeight = windowHeight <= 0 ? 1 : windowHeight;
+ mAspectRatio = (float)mWindowWidth / (float)mWindowHeight;
+
+ mVertFoVDegs = vertFoVDegs;
+ mZNear = zNear;
+ mZFar = zFar;
+ mOrthoExtent = orthoExtent;
+
+ config = service.getConfig();
+
+ screenSpaceMousePos = new Vec2f(config.windowWidth / 2.0f, config.windowHeight / 2.0f);
+
+// if (config.use3dDemo)
+
+ mOrthographicProjection = false;
+ mProjectionMatrix = Mat4f.createPerspectiveProjectionMatrix(mVertFoVDegs, mAspectRatio, mZNear, mZFar);
+
+// else
+// {
+// mOrthographicProjection = true;
+// mProjectionMatrix = Mat4f.createOrthographicProjectionMatrix(-mOrthoExtent, mOrthoExtent, mOrthoExtent, -mOrthoExtent, mZNear, mZFar);
+// }
+
+ // Setup the error callback to output to System.err
+ glfwSetErrorCallback(errorCallback = GLFWErrorCallback.createPrint(System.err));
+
+ // Initialize GLFW. Most GLFW functions will not work before doing this.
+ if ( !glfwInit() ) { throw new IllegalStateException("Unable to initialize GLFW"); }
+
+ // ----- Specify window hints -----
+ // Note: Window hints must be specified after glfwInit() (which resets them) and before glfwCreateWindow where the context is created.
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Request OpenGL version 3.3 (the minimum we can get away with)
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // We want a core profile without any deprecated functionality...
+ //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // ...however we do NOT want a forward compatible profile as they've removed line widths!
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // We want the window to be resizable
+ glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // We want the window to be visible (false makes it hidden after creation)
+ glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); // We want the window to take focus on creation
+ glfwWindowHint(GLFW_SAMPLES, 4); // Ask for 4x anti-aliasing (this doesn't mean we'll get it, though)
+
+ // Create the window
+ mWindowId = glfwCreateWindow(mWindowWidth, mWindowHeight, "LWJGL3 Test", NULL, NULL);
+ if (mWindowId == NULL) { throw new RuntimeException("Failed to create the GLFW window"); }
+
+ // Get the resolution of the primary monitor
+ GLFWVidMode vidmode = glfwGetVideoMode( glfwGetPrimaryMonitor() );
+ int windowHorizOffset = (vidmode.width() - mWindowWidth) / 2;
+ int windowVertOffset = (vidmode.height() - mWindowHeight) / 2;
+
+ glfwSetWindowPos(mWindowId, windowHorizOffset, windowVertOffset); // Center our window
+ glfwMakeContextCurrent(mWindowId); // Make the OpenGL context current
+ glfwSwapInterval(1); // Swap buffers every frame (i.e. enable vSync)
+
+ // This line is critical for LWJGL's interoperation with GLFW's OpenGL context, or any context that is managed externally.
+ // LWJGL detects the context that is current in the current thread, creates the ContextCapabilities instance and makes
+ // the OpenGL bindings available for use.
+ glfwMakeContextCurrent(mWindowId);
+
+ // Enumerate the capabilities of the current OpenGL context, loading forward compatible capabilities
+ GL.createCapabilities(true);
+
+ // Setup our keyboard, mouse and window resize callback functions
+ setupCallbacks();
+
+ // ---------- OpenGL settings -----------
+
+ // Set the clear color
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ // Specify the size of the viewport. Params: xOrigin, yOrigin, width, height
+ glViewport(0, 0, mWindowWidth, mWindowHeight);
+
+ // Enable depth testing
+ glDepthFunc(GL_LEQUAL);
+ glEnable(GL_DEPTH_TEST);
+
+ // When we clear the depth buffer, we'll clear the entire buffer
+ glClearDepth(1.0f);
+
+ // Enable blending to use alpha channels
+ // Note: blending must be enabled to use transparency / alpha values in our fragment shaders.
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ glfwShowWindow(mWindowId); // Make the window visible
+ }
+
+ // Constructor with some sensible projection matrix values hard-coded
+ public OpenGLWindow(Application application, Caliko service, int width, int height) { this(application, service, width, height, 35.0f, 1.0f, 5000.0f, 120.0f); }
+
+ /** Return a calculated ModelViewProjection matrix.
+ *
+ * This MVP matrix is the result of multiplying the projection matrix by the view matrix obtained from the camera, and
+ * as such is really a ProjectionView matrix or 'identity MVP', however you'd like to term it.
+ *
+ * If you want a MVP matrix specific to your model, simply multiply this matrix by your desired model matrix to create
+ * a MVP matrix specific to your model.
+ *
+ * @return A calculate ModelViewProjection matrix.
+ */
+ public Mat4f getMvpMatrix() { return mProjectionMatrix.times( service.getCamera().getViewMatrix() ); }
+
+ /**
+ * Return the projection matrix.
+ *
+ * @return The projection matrix.
+ */
+ public Mat4f getProjectionMatrix() { return mProjectionMatrix; }
+
+ /** Swap the front and back buffers to update the display. */
+ public void swapBuffers() { glfwSwapBuffers(mWindowId); }
+
+ /**
+ * Set the window title to the specified String argument.
+ *
+ * @param title The String that will be used as the title of the window.
+ */
+ public void setWindowTitle(String title) { glfwSetWindowTitle(mWindowId, title); }
+
+ /** Destroy the window, finish up glfw and release all callback methods. */
+ public void cleanup()
+ {
+ // Free the window callbacks and destroy the window
+ //glfwFreeCallbacks(mWindowId);
+ cursorPosCallback.close();
+ mouseButtonCallback.close();
+ windowSizeCallback.close();
+ keyCallback.close();
+
+ glfwDestroyWindow(mWindowId);
+
+ // Terminate GLFW and free the error callback
+ glfwTerminate();
+ glfwSetErrorCallback(null).free();
+ }
+
+ // Setup keyboard, mouse cursor, mouse button and window resize callback methods.
+ private void setupCallbacks()
+ {
+ // Key callback
+ glfwSetKeyCallback(mWindowId, keyCallback = GLFWKeyCallback.create( (long window, int key, int scancode, int action, int mods) ->
+ {
+ if (action == GLFW_PRESS)
+ {
+ switch (key)
+ {
+// // Setup demos
+// case GLFW_KEY_RIGHT:
+// if (config.use3dDemo)
+// {
+// if (config.demoNumber < CalikoDemoStructure3DEnum.values().length) { config.demoNumber++; }
+// }
+// else // 2D Demo mode
+// {
+// if (config.demoNumber < CalikoDemoStructure2DEnum.values().length) { config.demoNumber++; }
+// }
+// config.demo.setup(config.demoNumber);
+// break;
+// case GLFW_KEY_LEFT:
+// if (config.demoNumber > 1) { config.demoNumber--; }
+// config.demo.setup(config.demoNumber);
+// break;
+//
+// // Toggle fixed base mode
+// case GLFW_KEY_F:
+// config.fixedBaseMode = !config.fixedBaseMode;
+// config.demo.setFixedBaseMode(config.fixedBaseMode);
+// break;
+// // Toggle rotating bases
+// case GLFW_KEY_R:
+// config.rotateBasesMode = !config.rotateBasesMode;
+// break;
+
+ // Various drawing options
+ case GLFW_KEY_C:
+ config.drawConstraints = !config.drawConstraints;
+ break;
+ case GLFW_KEY_L:
+ config.drawLines = !config.drawLines;
+ break;
+ case GLFW_KEY_M:
+ config.drawModels = !config.drawModels;
+ break;
+ case GLFW_KEY_P:
+ mOrthographicProjection = !mOrthographicProjection;
+ if (mOrthographicProjection)
+ {
+ mProjectionMatrix = Mat4f.createOrthographicProjectionMatrix(-mOrthoExtent, mOrthoExtent, mOrthoExtent, -mOrthoExtent, mZNear, mZFar);
+ }
+ else
+ {
+ mProjectionMatrix = Mat4f.createPerspectiveProjectionMatrix(mVertFoVDegs, mAspectRatio, mZNear, mZFar);
+ }
+ break;
+ case GLFW_KEY_X:
+ config.drawAxes = !config.drawAxes;
+ break;
+
+ // Camera controls
+ case GLFW_KEY_W:
+ case GLFW_KEY_S:
+ case GLFW_KEY_A:
+ case GLFW_KEY_D:
+ // if (config.use3dDemo) { config.demo.handleCameraMovement(key, action); }
+ break;
+
+ // Close the window
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, true);
+ break;
+
+ // Cycle through / switch between 2D and 3D demos with the up and down cursors
+ case GLFW_KEY_UP:
+ case GLFW_KEY_DOWN:
+ config.use3dDemo = !config.use3dDemo;
+ config.demoNumber = 1;
+
+ // Viewing 2D demos?
+ if (!config.use3dDemo)
+ {
+ mOrthographicProjection = true;
+ mProjectionMatrix = Mat4f.createOrthographicProjectionMatrix(-mOrthoExtent, mOrthoExtent, mOrthoExtent, -mOrthoExtent, mZNear, mZFar);
+ // config.demo = new CalikoDemo2D(config.demoNumber);
+ }
+ else // Viewing 3D demos
+ {
+ mOrthographicProjection = false;
+ mProjectionMatrix = Mat4f.createPerspectiveProjectionMatrix(mVertFoVDegs, mAspectRatio, mZNear, mZFar);
+ // config.demo = new CalikoDemo3D(config.demoNumber);
+ }
+ break;
+
+ // Dynamic add/remove bones for first demo
+// case GLFW_KEY_COMMA:
+// if (config.demoNumber == 1 && config.structure.getChain(0).getNumBones() > 1)
+// {
+// config.structure.getChain(0).removeBone(0);
+// }
+// break;
+// case GLFW_KEY_PERIOD:
+// if (config.demoNumber == 1)
+// {
+// config.structure.getChain(0).addConsecutiveBone(config.X_AXIS, config.defaultBoneLength);
+// }
+// break;
+
+ case GLFW_KEY_SPACE:
+ application.paused = !application.paused;
+ break;
+
+ } // End of switch
+
+ }
+ else if (action == GLFW_REPEAT || action == GLFW_RELEASE) // Camera must also handle repeat or release actions
+ {
+ switch (key)
+ {
+ case GLFW_KEY_W:
+ case GLFW_KEY_S:
+ case GLFW_KEY_A:
+ case GLFW_KEY_D:
+ //if (config.use3dDemo) { config.demo.handleCameraMovement(key, action); }
+ break;
+ }
+ }
+ }));
+
+ // Mouse cursor position callback
+ glfwSetCursorPosCallback(mWindowId, cursorPosCallback = GLFWCursorPosCallback.create( (long windowId, double mouseX, double mouseY) ->
+ {
+ // Update the screen space mouse location
+ screenSpaceMousePos.set( (float)mouseX, (float)mouseY );
+
+ // If we're holding down the LMB, then...
+ if (config.leftMouseButtonDown)
+ {
+ // ...in the 3D demo we update the camera look direction...
+ if (config.use3dDemo)
+ {
+ service.getCamera().handleMouseMove(mouseX, mouseY);
+ }
+ else // ...while in the 2D demo we update the 2D target.
+ {
+ // Convert the mouse position in screen-space coordinates to our orthographic world-space coordinates
+// worldSpaceMousePos.set( Utils.convertRange(screenSpaceMousePos.x, 0.0f, mWindowWidth, -mOrthoExtent, mOrthoExtent),
+// -Utils.convertRange(screenSpaceMousePos.y, 0.0f, mWindowHeight, -mOrthoExtent, mOrthoExtent) );
+//
+// CalikoDemo2D.mStructure.solveForTarget(worldSpaceMousePos);
+ }
+ }
+ }));
+
+ // Mouse button callback
+ glfwSetMouseButtonCallback(mWindowId, mouseButtonCallback = GLFWMouseButtonCallback.create( (long windowId, int button, int action, int mods) ->
+ {
+ // If the left mouse button was the button that invoked the callback...
+ if (button == GLFW_MOUSE_BUTTON_1)
+ {
+ // ...then set the LMB status flag accordingly
+ // Note: We cannot simply toggle the flag here as double-clicking the title bar to fullscreen the window confuses it and we
+ // then end up mouselook-ing without the LMB being held down!
+ if (action == GLFW_PRESS) { config.leftMouseButtonDown = true; } else { config.leftMouseButtonDown = false; }
+
+ if (config.use3dDemo)
+ {
+ // Immediately set the cursor position to the centre of the screen so our view doesn't "jump" on first cursor position change
+ glfwSetCursorPos(windowId, ((double)mWindowWidth / 2), ((double)mWindowHeight / 2) );
+
+ switch (action)
+ {
+ case GLFW_PRESS:
+ // Make the mouse cursor hidden and put it into a 'virtual' mode where its values are not limited
+ glfwSetInputMode(mWindowId, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ break;
+
+ case GLFW_RELEASE:
+ // Restore the mouse cursor to normal and reset the camera last cursor position to be the middle of the window
+ glfwSetInputMode(windowId, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ service.getCamera().resetLastCursorPosition();
+ break;
+ }
+ }
+ else
+ {
+ // Convert the mouse position in screen-space coordinates to our orthographic world-space coordinates
+ worldSpaceMousePos.set( Utils.convertRange(screenSpaceMousePos.x, 0.0f, mWindowWidth, -mOrthoExtent, mOrthoExtent),
+ -Utils.convertRange(screenSpaceMousePos.y, 0.0f, mWindowHeight, -mOrthoExtent, mOrthoExtent) );
+
+ // CalikoDemo2D.mStructure.solveForTarget(worldSpaceMousePos);
+ }
+
+ // Nothing needs be done in 2D demo mode - the config.leftMouseButtonDown flag plus the mouse cursor handler take care of it.
+ }
+ }));
+
+ // Window size callback
+ glfwSetWindowSizeCallback(mWindowId, windowSizeCallback = GLFWWindowSizeCallback.create( (long windowId, int windowWidth, int windowHeight) ->
+ {
+ // Update our window width and height and recalculate the aspect ratio
+ if (windowWidth <= 0) { windowWidth = 1; }
+ if (windowHeight <= 0) { windowHeight = 1; }
+ mWindowWidth = windowWidth;
+ mWindowHeight = windowHeight;
+ mAspectRatio = (float)mWindowWidth / (float)mWindowHeight;
+
+ // Let our camera know about the new size so it can correctly recentre the mouse cursor
+ service.getCamera().updateWindowSize(windowWidth, windowHeight);
+
+ // Update our viewport
+ glViewport(0, 0, mWindowWidth, mWindowHeight);
+
+ // Recalculate our projection matrix
+ if (mOrthographicProjection)
+ {
+ mProjectionMatrix = Mat4f.createOrthographicProjectionMatrix(-mOrthoExtent, mOrthoExtent, mOrthoExtent, -mOrthoExtent, mZNear, mZFar);
+ }
+ else
+ {
+ mProjectionMatrix = Mat4f.createPerspectiveProjectionMatrix(mVertFoVDegs, mAspectRatio, mZNear, mZFar);
+ }
+ }));
+
+ } // End of setupCallbacks method
+
+} // End of OpenGLWindow class
\ No newline at end of file
diff --git a/src/main/java/org/myrobotlab/framework/Outbox.java b/src/main/java/org/myrobotlab/framework/Outbox.java
index 3558028dea..5e323a1252 100644
--- a/src/main/java/org/myrobotlab/framework/Outbox.java
+++ b/src/main/java/org/myrobotlab/framework/Outbox.java
@@ -365,7 +365,7 @@ public void reset() {
* Safe detach for single subscriber
*
* @param service
- * the name of the listener to detach
+ * the name of the listener to detach
*
*/
synchronized public void detach(String service) {
diff --git a/src/main/java/org/myrobotlab/framework/ProxyInterceptor.java b/src/main/java/org/myrobotlab/framework/ProxyInterceptor.java
index c9c6aefef1..dddd190593 100644
--- a/src/main/java/org/myrobotlab/framework/ProxyInterceptor.java
+++ b/src/main/java/org/myrobotlab/framework/ProxyInterceptor.java
@@ -53,6 +53,7 @@ public ProxyInterceptor(String name, String id, String typeKey) {
* Name, Id, FullName, TypeKey and list of interfaces
* are all available during registration, these methods
* should not later go out to the client to resolve.
+ *
* @return
*/
public String getId() {
@@ -60,8 +61,9 @@ public String getId() {
}
/**
- * Name is availble at registration, don't need to ask the
+ * Name is availble at registration, don't need to ask the
* remote service again
+ *
* @return
*/
public String getName() {
@@ -70,6 +72,7 @@ public String getName() {
/**
* Convenience method
+ *
* @return
*/
public String getFullName() {
@@ -78,6 +81,7 @@ public String getFullName() {
/**
* Given on registration, don't need client to be queried for it
+ *
* @return
*/
public String getTypeKey() {
@@ -94,7 +98,7 @@ public String toString() {
public String getSimpleName() {
return name;
}
-
+
@RuntimeType
public Object intercept(@Origin Method method, @AllArguments Object... args)
throws InterruptedException, TimeoutException {
diff --git a/src/main/java/org/myrobotlab/framework/Service.java b/src/main/java/org/myrobotlab/framework/Service.java
index 8f0d4ad2bf..5780e1241d 100644
--- a/src/main/java/org/myrobotlab/framework/Service.java
+++ b/src/main/java/org/myrobotlab/framework/Service.java
@@ -1989,7 +1989,7 @@ synchronized public ServiceInterface startPeer(String peerKey) {
broadcastState();
return Runtime.start(peer.name);
}
-
+
@Override
synchronized public void startPeers(String[] peerKeys) {
diff --git a/src/main/java/org/myrobotlab/programab/BotInfo.java b/src/main/java/org/myrobotlab/programab/BotInfo.java
index 7e8e416cbf..20ee51561d 100644
--- a/src/main/java/org/myrobotlab/programab/BotInfo.java
+++ b/src/main/java/org/myrobotlab/programab/BotInfo.java
@@ -12,6 +12,7 @@
import org.alicebot.ab.Bot;
import org.myrobotlab.io.FileIO;
import org.myrobotlab.logging.LoggerFactory;
+import org.myrobotlab.programab.handlers.sraix.MrlSraixHandler;
import org.myrobotlab.service.ProgramAB;
import org.slf4j.Logger;
@@ -23,7 +24,7 @@ public class BotInfo {
transient public final static Logger log = LoggerFactory.getLogger(BotInfo.class);
- public String name;
+ public String currentBotName;
public File path;
public Properties properties = new Properties();
private transient Bot bot;
@@ -36,17 +37,17 @@ public class BotInfo {
public String img;
public BotInfo(ProgramAB programab, File path) {
- this.name = path.getName();
+ this.currentBotName = path.getName();
this.path = path;
this.programab = programab;
- programab.info("found bot %s", name);
+ programab.info("found bot %s", currentBotName);
try {
FileInputStream fis = new FileInputStream(FileIO.gluePaths(path.getAbsolutePath(), "manifest.txt"));
properties.load(new InputStreamReader(fis, Charset.forName("UTF-8")));
fis.close();
log.info("loaded properties");
} catch (FileNotFoundException e) {
- programab.warn("bot %s does not have a manifest.txt", name);
+ programab.warn("bot %s does not have a manifest.txt", currentBotName);
} catch (Exception e) {
log.error("BotInfo threw", e);
}
@@ -62,13 +63,15 @@ public synchronized Bot getBot() {
if (bot == null) {
// lazy loading of bot - created on the first use
if (properties.containsKey("locale")) {
- bot = new Bot(name, path.getAbsolutePath(), java.util.Locale.forLanguageTag((String) properties.get("locale")));
+ bot = new Bot(currentBotName, path.getAbsolutePath(),
+ java.util.Locale.forLanguageTag((String) properties.get("locale")));
bot.listener = programab;
} else {
if (programab.getLocaleTag() == null) {
- bot = new Bot(name, path.getAbsolutePath());
+ bot = new Bot(currentBotName, path.getAbsolutePath());
} else {
- bot = new Bot(name, path.getAbsolutePath(), java.util.Locale.forLanguageTag(programab.getLocaleTag()));
+ bot = new Bot(currentBotName, path.getAbsolutePath(),
+ java.util.Locale.forLanguageTag(programab.getLocaleTag()));
}
bot.listener = programab;
}
@@ -131,7 +134,7 @@ public void removeProperty(String name2) {
@Override
public String toString() {
- return String.format("%s - %s", name, path);
+ return String.format("%s - %s", currentBotName, path);
}
}
diff --git a/src/main/java/org/myrobotlab/programab/MrlSraixHandler.java b/src/main/java/org/myrobotlab/programab/MrlSraixHandler.java
deleted file mode 100755
index d1b2808b2e..0000000000
--- a/src/main/java/org/myrobotlab/programab/MrlSraixHandler.java
+++ /dev/null
@@ -1,211 +0,0 @@
-package org.myrobotlab.programab;
-
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.regex.Matcher;
-
-import org.alicebot.ab.Chat;
-import org.alicebot.ab.Sraix;
-import org.alicebot.ab.SraixHandler;
-import org.myrobotlab.codec.CodecUtils;
-import org.myrobotlab.framework.Message;
-import org.myrobotlab.framework.interfaces.ServiceInterface;
-import org.myrobotlab.logging.LoggerFactory;
-import org.myrobotlab.service.ProgramAB;
-import org.myrobotlab.service.Runtime;
-import org.myrobotlab.service.data.SearchResults;
-import org.myrobotlab.service.interfaces.SearchPublisher;
-import org.myrobotlab.string.StringUtil;
-// import org.nd4j.shade.jackson.dataformat.xml.XmlMapper;
-import org.slf4j.Logger;
-
-import com.fasterxml.jackson.dataformat.xml.XmlMapper;
-
-
-public class MrlSraixHandler implements SraixHandler {
- transient public final static Logger log = LoggerFactory.getLogger(MrlSraixHandler.class);
-
- private ProgramAB programab = null;
-
- public MrlSraixHandler() {
-
- }
-
- public MrlSraixHandler(ProgramAB programab) {
- this.programab = programab;
- }
-
- @Override
- public String sraix(Chat chatSession, String input, String defaultResponse, String hint, String host, String botid, String apiKey, String limit, Locale locale) {
- log.debug("MRL Sraix handler! Input {}", input);
-
- // FIXME - "list of AIs in priority order to attempt to handle request
- // best synopsis of sraix I've found - https://gist.github.com/onlurking/f6431e672cfa202c09a7c7cf92ac8a8b
- try {
- XmlMapper xmlMapper = new XmlMapper();
- Oob oob = xmlMapper.readValue(input, Oob.class);
- StringBuilder responseText = new StringBuilder();
- if (oob.mrljson != null) {
- Message[] msgs = CodecUtils.fromJson(oob.mrljson, Message[].class);
- for (Message msg: msgs) {
- msg.sender = programab.getName();
- msg.sendingMethod = "sraix";
- // buffered asynchronous - use invoke synchronous
- // programab.in(msg);
- // invoking to keep it synchronous
- ServiceInterface si = Runtime.getService(msg.getName());
- Object ret = si.invoke(msg.method, msg.data);
- if (ret != null) {
- responseText.append(ret.toString());
- }
- }
- return responseText.toString();
- }
- log.info("found oob {}", oob);
- } catch (Exception e) {
- // programab.error("threw on input %s", input);
- }
-
- // the INPUT has the string we care about. if this is an OOB tag, let's
- // evaluate it and return the result.
- if (containsOOB(input)) {
- String response = processInlineOOB(input);
- return response;
- } else if (programab != null && programab.getPeer("search") != null) {
- try {
- SearchPublisher search = (SearchPublisher) programab.getPeer("search");
- if (search != null) {
- SearchResults results = search.search(input);
- String searchResponse = results.getTextAndImages();
-
- if (searchResponse == null || searchResponse.length() == 0) {
- Session session = programab.getSession();
- // TODO - perhaps more rich codes for details of failure
- // Response r = session.getResponse("SRAIXFAILED_WIKIPEDIA " + input);
- Response r = session.getResponse("SRAIXFAILED " + input);
- return r.msg;
- }
- return searchResponse;
- } else {
- // TODO - perhaps more rich codes for details of failure
- // Response r = programab.getResponse("SRAIXFAILED_WIKIPEDIA_NOT_AVAILABLE");
- Session session = programab.getSession();
- Response r = session.getResponse("SRAIXFAILED " + input);
- return r.msg;
- }
-
- } catch (Exception e) {
- return "sorry, I cannot search now " + e.getMessage();
- }
- } else {
- // fall back to default behavior of pannous / pandorabots?
- // TODO: expose pandora bots here if botid is set?
- // TODO: enable call out to an official MRL hosted NLU service/ knowedge
- // service.
-
- String response = Sraix.sraixPannous(input, hint, chatSession, locale);
- if (StringUtil.isEmpty(response)) {
- return defaultResponse;
- } else {
- // clean up the response a bit.
- response = cleanPannousResponse(response);
- return response;
- }
- }
- }
-
- private String cleanPannousResponse(String response) {
- String clean = response.replaceAll("\\(Answers.com\\)", "").trim();
- return clean;
- }
-
- private boolean containsOOB(String text) {
- Matcher oobMatcher = OOBPayload.oobPattern.matcher(text);
- return oobMatcher.matches();
- }
-
- // TODO override it inside programAB to share methods and publish OOB
- private String processInlineOOB(String text) {
- // Find any oob tags
- StringBuilder responseBuilder = new StringBuilder();
- ArrayList payloads = new ArrayList();
- Matcher oobMatcher = OOBPayload.oobPattern.matcher(text);
- int start = 0;
- while (oobMatcher.find()) {
- // We found some OOB text.
- // assume only one OOB in the text?
- // everything from the start to the end of this
- responseBuilder.append(text.substring(start, oobMatcher.start()));
- // update the end to be
- // next segment is from the end of this one to the start of the next one.
- start = oobMatcher.end();
- String oobPayload = oobMatcher.group(0);
- Matcher mrlMatcher = OOBPayload.mrlPattern.matcher(oobPayload);
- while (mrlMatcher.find()) {
- String mrlPayload = mrlMatcher.group(0);
- OOBPayload payload = parseOOB(mrlPayload);
- Object result = invokeOOBPayloads(payloads, mrlPayload, payload);
- if (result != null && result.getClass().isArray()) {
- Object[] objects = (Object[]) result;
- for (Object o : objects) {
- responseBuilder.append(o.toString() + " ");
- }
- } else {
-
- if (result != null) {
- responseBuilder.append(result);
- }
- }
- log.info("OOB PROCESSING RESULT: {}", result);
- }
- }
- // append the last part. (assume the start is set to the end of the last
- // match..
- // or zero if no matches found.
- responseBuilder.append(text.substring(start));
- return responseBuilder.toString();
- }
-
- private Object invokeOOBPayloads(ArrayList payloads, String mrlPayload, OOBPayload payload) {
- payloads.add(payload);
- // grab service and invoke method.
- ServiceInterface s = Runtime.getService(payload.getServiceName());
- if (s == null) {
- log.warn("Service name in OOB/MRL tag unknown. {}", mrlPayload);
- return null;
- }
- Object result = null;
- if (payload.getParams() != null) {
- result = s.invoke(payload.getMethodName(), payload.getParams().toArray());
- } else {
- result = s.invoke(payload.getMethodName());
- }
- return result;
- }
-
- private OOBPayload parseOOB(String oobPayload) {
-
- // TODO: fix the damn double encoding issue.
- // we have user entered text in the service/method and params values.
- // grab the service
- Matcher serviceMatcher = OOBPayload.servicePattern.matcher(oobPayload);
- serviceMatcher.find();
- String serviceName = serviceMatcher.group(1);
-
- Matcher methodMatcher = OOBPayload.methodPattern.matcher(oobPayload);
- methodMatcher.find();
- String methodName = methodMatcher.group(1);
-
- Matcher paramMatcher = OOBPayload.paramPattern.matcher(oobPayload);
- ArrayList params = new ArrayList();
- while (paramMatcher.find()) {
- // We found some OOB text.
- // assume only one OOB in the text?
- String param = paramMatcher.group(1);
- params.add(param);
- }
- OOBPayload payload = new OOBPayload(serviceName, methodName, params);
- return payload;
-
- }
-}
diff --git a/src/main/java/org/myrobotlab/programab/OOBPayload.java b/src/main/java/org/myrobotlab/programab/OOBPayload.java
deleted file mode 100644
index c9984a6929..0000000000
--- a/src/main/java/org/myrobotlab/programab/OOBPayload.java
+++ /dev/null
@@ -1,176 +0,0 @@
-package org.myrobotlab.programab;
-
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.lang3.StringUtils;
-import org.myrobotlab.framework.Message;
-import org.myrobotlab.framework.interfaces.ServiceInterface;
-import org.myrobotlab.logging.LoggerFactory;
-import org.myrobotlab.service.ProgramAB;
-import org.myrobotlab.service.Runtime;
-import org.slf4j.Logger;
-
-public class OOBPayload {
-
- transient public final static Logger log = LoggerFactory.getLogger(OOBPayload.class);
- // TODO: something better than regex to parse the xml. (Problem is that the
- // service/method/param values
- // could end up double encoded ... So we had to switch to hamd crafting the
- // aiml for the oob/mrl tag.
- public transient static final Pattern oobPattern = Pattern.compile(".*?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
- public transient static final Pattern mrlPattern = Pattern.compile(".*?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
- public transient static final Pattern servicePattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
- public transient static final Pattern methodPattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
- public transient static final Pattern paramPattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
-
- private String serviceName;
- private String methodName;
- private ArrayList params;
-
- public OOBPayload() {
- // TODO: remove the default constructor
- };
-
- public OOBPayload(String serviceName, String methodName, ArrayList params) {
- this.serviceName = serviceName;
- this.methodName = methodName;
- this.params = params;
- }
-
- public String getMethodName() {
- return methodName;
- }
-
- public ArrayList getParams() {
- return params;
- }
-
- public String getServiceName() {
- return serviceName;
- }
-
- public void setMethodName(String methodName) {
- this.methodName = methodName;
- }
-
- public void setParams(ArrayList params) {
- this.params = params;
- }
-
- public void setServiceName(String serviceName) {
- this.serviceName = serviceName;
- }
-
- public static String asOOBTag(OOBPayload payload) {
- // TODO: this isn't really safe as XML/AIML.. but we don't want to end up
- // double encoding things like
- // the important tags... So, for now, it's just wrapped in the tags.
- StringBuilder oobBuilder = new StringBuilder();
- oobBuilder.append("");
- oobBuilder.append("");
- oobBuilder.append("");
- oobBuilder.append(payload.getServiceName());
- oobBuilder.append("");
- oobBuilder.append("");
- oobBuilder.append(payload.getMethodName());
- oobBuilder.append("");
- for (String param : payload.params) {
- oobBuilder.append("");
- // TODO: this could be problematic if the param contains XML chars that
- // are not AIML ...
- oobBuilder.append(param);
- oobBuilder.append("");
- }
- oobBuilder.append("");
- oobBuilder.append("");
- return oobBuilder.toString();
- }
-
- public static String asBlockingOOBTag(OOBPayload oobTag) {
- return "" + OOBPayload.asOOBTag(oobTag) + "";
- }
-
- public static OOBPayload fromString(String oobPayload) {
-
- // TODO: fix the damn double encoding issue.
- // we have user entered text in the service/method
- // and params values.
- // grab the service
-
- Matcher serviceMatcher = servicePattern.matcher(oobPayload);
- serviceMatcher.find();
- String serviceName = serviceMatcher.group(1);
-
- Matcher methodMatcher = methodPattern.matcher(oobPayload);
- methodMatcher.find();
- String methodName = methodMatcher.group(1);
-
- Matcher paramMatcher = paramPattern.matcher(oobPayload);
- ArrayList params = new ArrayList();
- while (paramMatcher.find()) {
- // We found some OOB text.
- // assume only one OOB in the text?
- String param = paramMatcher.group(1);
- params.add(param);
- }
- OOBPayload payload = new OOBPayload(serviceName, methodName, params);
- // log.info(payload.toString());
- return payload;
- }
-
- public static boolean invokeOOBPayload(OOBPayload payload, String sender, boolean blocking) {
- ServiceInterface s = Runtime.getService(payload.getServiceName());
- // the service must exist and the method name must be set.
- if (s == null || StringUtils.isEmpty(payload.getMethodName())) {
- return false;
- }
-
- if (!blocking) {
- s.in(Message.createMessage(sender, payload.getServiceName(), payload.getMethodName(), payload.getParams().toArray()));
- // non-blocking.. fire and forget!
- return true;
- }
-
- // TODO: should you be able to be synchronous for this
- // execution?
- Object result = null;
- if (payload.getParams() != null) {
- result = s.invoke(payload.getMethodName(), payload.getParams().toArray());
- } else {
- result = s.invoke(payload.getMethodName());
- }
- log.info("OOB PROCESSING RESULT: {}", result);
- return true;
- }
-
- public static ArrayList extractOOBPayloads(String text, ProgramAB programAB) {
- ArrayList payloads = new ArrayList();
- Matcher oobMatcher = OOBPayload.oobPattern.matcher(text);
- while (oobMatcher.find()) {
- // We found some OOB text.
- // assume only one OOB in the text?
- String oobPayload = oobMatcher.group(0);
- Matcher mrlMatcher = OOBPayload.mrlPattern.matcher(oobPayload);
- while (mrlMatcher.find()) {
- String mrlPayload = mrlMatcher.group(0);
- OOBPayload payload = OOBPayload.fromString(mrlPayload);
- payloads.add(payload);
- // TODO: maybe we dont' want this?
- // Notifiy endpoints
- programAB.invoke("publishOOBText", mrlPayload);
- // grab service and invoke method.
-
- }
- }
- return payloads;
- }
-
- public static String removeOOBFromString(String res) {
- Matcher matcher = OOBPayload.oobPattern.matcher(res);
- res = matcher.replaceAll("");
- return res;
- }
-
-}
diff --git a/src/main/java/org/myrobotlab/programab/Oob.java b/src/main/java/org/myrobotlab/programab/Oob.java
deleted file mode 100644
index d6c8d2bf81..0000000000
--- a/src/main/java/org/myrobotlab/programab/Oob.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.myrobotlab.programab;
-
-public class Oob {
-
- public class Mrl {
- public String service;
- public String method;
- public Object[] param;
- }
-
- public String mrljson;
- public String mrl;
-}
-
diff --git a/src/main/java/org/myrobotlab/programab/PredicateEvent.java b/src/main/java/org/myrobotlab/programab/PredicateEvent.java
deleted file mode 100644
index ef4a36d8ef..0000000000
--- a/src/main/java/org/myrobotlab/programab/PredicateEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.myrobotlab.programab;
-
-/**
- * Pojo for state change of one of ProgramAB's state info
- * @author GroG
- *
- */
-public class PredicateEvent {
- /**
- * unique identifier for the session user and bot
- */
- public String id;
- /**
- * name of the predicate changed
- */
- public String name;
-
- // public String previousValue;
-
- /**
- * new value
- */
- public String value;
- public String botName;
- public String userName;
-
- @Override
- public String toString() {
- return String.format("%s %s=%s", id, name, value);
- }
-}
diff --git a/src/main/java/org/myrobotlab/programab/Response.java b/src/main/java/org/myrobotlab/programab/Response.java
index 128ae8cfc0..a2802196b7 100644
--- a/src/main/java/org/myrobotlab/programab/Response.java
+++ b/src/main/java/org/myrobotlab/programab/Response.java
@@ -3,6 +3,8 @@
import java.util.Date;
import java.util.List;
+import org.myrobotlab.programab.models.Mrl;
+
/**
* FIXME - this class should become a more generalized AI response data object
* in org.myrobotlab.data so that other AI systems (and search engines) can fill
@@ -29,12 +31,14 @@ public class Response {
/**
* filtered oob data
*/
- public List payloads;
+ public List payloads;
- public Response(String userName, String botName, String msg, List payloads) {
+ public Response(String userName, String botName, String msg, List payloads) {
this.botName = botName;
this.userName = userName;
this.msg = msg;
+
+ // what is this for ?
this.payloads = payloads;
}
@@ -48,7 +52,7 @@ public String toString() {
str.append("Msg:" + msg + ", ");
str.append("Payloads:[");
if (payloads != null) {
- for (OOBPayload payload : payloads) {
+ for (Mrl payload : payloads) {
str.append(payload.toString() + ", ");
}
}
diff --git a/src/main/java/org/myrobotlab/programab/Session.java b/src/main/java/org/myrobotlab/programab/Session.java
index a234310e57..b56cb4df91 100644
--- a/src/main/java/org/myrobotlab/programab/Session.java
+++ b/src/main/java/org/myrobotlab/programab/Session.java
@@ -3,8 +3,8 @@
import java.io.File;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
@@ -13,6 +13,10 @@
import org.alicebot.ab.Predicates;
import org.myrobotlab.io.FileIO;
import org.myrobotlab.logging.LoggerFactory;
+import org.myrobotlab.programab.handlers.oob.OobProcessor;
+import org.myrobotlab.programab.models.Event;
+import org.myrobotlab.programab.models.Mrl;
+import org.myrobotlab.programab.models.Template;
import org.myrobotlab.service.ProgramAB;
import org.myrobotlab.service.config.ProgramABConfig;
import org.slf4j.Logger;
@@ -27,69 +31,102 @@ public class Session {
transient public final static Logger log = LoggerFactory.getLogger(ProgramAB.class);
+ /**
+ * name of the user that owns this session
+ */
public String userName;
- public boolean processOOB = true;
+
+ /**
+ * last time the bot responded
+ */
public Date lastResponseTime = null;
+
+ /**
+ * bot will prompt users if enabled trolling is true after
+ * maxConversationDelay has passed
+ */
public boolean enableTrolling = false;
- // Number of milliseconds before the robot starts talking on its own.
+
+ /**
+ * Number of milliseconds before the robot starts talking on its own.
+ */
public int maxConversationDelay = 5000;
- // FIXME - could be transient ??
- transient public BotInfo botInfo;
+ /**
+ * general bot information
+ */
+ public transient BotInfo botInfo;
+
+ /**
+ * interface to program-ab
+ */
public transient Chat chat;
- transient ProgramAB programab;
+ /**
+ * service that manages this session
+ */
+ private transient ProgramAB programab;
+ /**
+ * current file associated with this user and session
+ */
public File predicatesFile;
- // public Map predicates = new TreeMap<>();
- public Predicates predicates = null;
+ /**
+ * predicate data associated with this session
+ */
+ protected Predicates predicates = null;
- // current topic of this session
+ /**
+ * current topic of this session
+ */
public String currentTopic = null;
/**
* Session for a user and bot
*
* @param programab
- * program ab for this session
+ * program ab for this session
* @param userName
- * username
+ * userName
* @param botInfo
- * the bot for the session
+ * the bot for the session
*
*/
public Session(ProgramAB programab, String userName, BotInfo botInfo) {
this.programab = programab;
this.userName = userName;
this.botInfo = botInfo;
+ this.chat = loadChat();
+ predicates = chat.predicates;
+
+ Event event = new Event(programab.getName(), userName, null, null);
+ programab.invoke("publishSession", event);
+
+ ProgramABConfig config = programab.getConfig();
+ if (config.startTopic != null) {
+ chat.predicates.put("topic", config.startTopic);
+ }
+
+ this.maxConversationDelay = config.maxConversationDelay;
+ this.enableTrolling = config.enableTrolling;
}
- /**
- * lazy loading chat
- *
- * task to save predicates and getting responses will eventually call getBot
- * we don't want initialization to create 2 when only one is needed
- *
- * @return
- */
private synchronized Chat getChat() {
- if (chat == null) {
- chat = new Chat(botInfo.getBot());
- // loading predefined predicates - if they exist
- File userPredicates = new File(FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", userName)));
- if (userPredicates.exists()) {
- predicatesFile = userPredicates;
- chat.predicates.getPredicateDefaults(userPredicates.getAbsolutePath());
- }
-
- ProgramABConfig config = (ProgramABConfig)programab.getConfig();
- if (config.startTopic != null){
- chat.predicates.put("topic", config.startTopic);
- }
+ return chat;
+ }
+
+ private Chat loadChat() {
+ Chat chat = new Chat(botInfo.getBot());
+ // loading predefined predicates - if they exist
+ File userPredicates = new File(
+ FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", userName)));
+ if (userPredicates.exists()) {
+ predicatesFile = userPredicates;
+ chat.predicates.getPredicateDefaults(userPredicates.getAbsolutePath());
}
- predicates = chat.predicates;
+
return chat;
}
@@ -103,9 +140,10 @@ public void savePredicates() {
sb.append(predicate + ":" + value + "\n");
}
}
- File predicates = new File(FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", userName)));
+ File predicates = new File(
+ FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", userName)));
predicates.getParentFile().mkdirs();
- log.info("Bot : {} User : {} Predicates Filename : {} ", botInfo.name, userName, predicates);
+ log.info("bot : {} user : {} saving predicates filename : {} ", botInfo.currentBotName, userName, predicates);
try {
FileWriter writer = new FileWriter(predicates, StandardCharsets.UTF_8);
writer.write(sb.toString());
@@ -118,6 +156,7 @@ public void savePredicates() {
/**
* Get all current predicate names and values
+ *
* @return
*/
public Map getPredicates() {
@@ -127,38 +166,28 @@ public Map getPredicates() {
}
public Response getResponse(String inText) {
+ try {
+ String returnText = getChat().multisentenceRespond(inText);
+ String xml = String.format("%s", returnText);
+ Template template = XmlParser.parseTemplate(xml);
- String text = getChat().multisentenceRespond(inText);
-
- // Find any oob tags
- ArrayList oobTags = OOBPayload.extractOOBPayloads(text, programab);
+ OobProcessor handler = programab.getOobProcessor();
+ handler.process(template.oob, true); // block by default
- // invoke them all if configured to do so
- if (processOOB) {
- for (OOBPayload payload : oobTags) {
- // assumption is this is non blocking invoking!
- boolean oobRes = OOBPayload.invokeOOBPayload(payload, programab.getName(), false);
- if (!oobRes) {
- // there was a failure invoking
- log.warn("Failed to invoke OOB/MRL tag : {}", OOBPayload.asOOBTag(payload));
- }
- }
- }
-
- // strip any oob tags if found
- if (oobTags.size() > 0) {
- text = OOBPayload.removeOOBFromString(text).trim();
+ List mrl = template.oob != null ? template.oob.mrl : null;
+ // returned all text inside template but outside oob
+ Response response = new Response(userName, botInfo.currentBotName, template.text, mrl);
+ return response;
+ } catch (Exception e) {
+ programab.error(e);
}
-
- Response response = new Response(userName, botInfo.name, text, oobTags);
- return response;
-
+ return new Response(userName, botInfo.currentBotName, "", null);
}
public Chat reload() {
botInfo.reload();
- chat = null;
- return getChat();
+ chat = loadChat();
+ return chat;
}
public void remove(String predicateName) {
@@ -173,4 +202,12 @@ public String getPredicate(String predicateName) {
return getChat().predicates.get(predicateName);
}
+ public String getUsername() {
+ return userName;
+ }
+
+ public Object getBotType() {
+ return botInfo.currentBotName;
+ }
+
}
diff --git a/src/main/java/org/myrobotlab/programab/XmlParser.java b/src/main/java/org/myrobotlab/programab/XmlParser.java
new file mode 100644
index 0000000000..51051af7db
--- /dev/null
+++ b/src/main/java/org/myrobotlab/programab/XmlParser.java
@@ -0,0 +1,31 @@
+package org.myrobotlab.programab;
+
+import org.myrobotlab.programab.models.Sraix;
+import org.myrobotlab.programab.models.Template;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+
+/**
+ * Thread safe fasterjackson xml parser.
+ *
+ * @author GroG
+ *
+ */
+public class XmlParser {
+
+ public static Template parseTemplate(String xml) throws JsonMappingException, JsonProcessingException {
+ ThreadLocal xmlMapperThreadLocal = ThreadLocal.withInitial(XmlMapper::new);
+ XmlMapper xmlMapper = xmlMapperThreadLocal.get();
+ Template template = xmlMapper.readValue(xml, Template.class);
+ return template;
+ }
+
+ public static Sraix parseSraix(String xml) throws JsonMappingException, JsonProcessingException {
+ ThreadLocal xmlMapperThreadLocal = ThreadLocal.withInitial(XmlMapper::new);
+ XmlMapper xmlMapper = xmlMapperThreadLocal.get();
+ return xmlMapper.readValue(xml, Sraix.class);
+ }
+
+}
diff --git a/src/main/java/org/myrobotlab/programab/handlers/oob/OobProcessor.java b/src/main/java/org/myrobotlab/programab/handlers/oob/OobProcessor.java
new file mode 100644
index 0000000000..dfe19e5cb6
--- /dev/null
+++ b/src/main/java/org/myrobotlab/programab/handlers/oob/OobProcessor.java
@@ -0,0 +1,86 @@
+package org.myrobotlab.programab.handlers.oob;
+
+import java.util.List;
+
+import org.myrobotlab.codec.CodecUtils;
+import org.myrobotlab.framework.Message;
+import org.myrobotlab.programab.models.Mrl;
+import org.myrobotlab.programab.models.Oob;
+import org.myrobotlab.service.ProgramAB;
+
+public class OobProcessor {
+
+ private transient ProgramAB programab;
+
+ protected int maxBlockTime = 2000;
+
+ public OobProcessor(ProgramAB programab) {
+ this.programab = programab;
+ }
+
+ public Message toMsg(Mrl mrl) {
+ Object[] data = null;
+ if (mrl.params != null) {
+ data = new Object[mrl.params.size()];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = mrl.params.get(i).trim();
+ }
+ }
+ String service = mrl.service == null ? null : mrl.service.trim();
+ return Message.createMessage(programab.getName(), service, mrl.method.trim(), data);
+ }
+
+ public String process(Oob oob, boolean block) {
+ StringBuilder sb = new StringBuilder();
+
+ // FIXME dynamic way of registering oobs
+ if (oob != null) {
+ // Process
+ if (oob.mrl != null) {
+ List mrls = oob.mrl;
+ for (Mrl mrl : mrls) {
+ if (!block) {
+ // programab.out(toMsg(mrl));
+ programab.info("sending without blocking %s", toMsg(mrl));
+ programab.send(toMsg(mrl));
+ } else {
+ try {
+ programab.info("sendingBlocking without blocking %s", toMsg(mrl));
+ Object o = programab.sendBlocking(toMsg(mrl), maxBlockTime);
+ if (o != null) {
+ sb.append(o);
+ }
+ } catch (Exception e) {
+ programab.error(e);
+ }
+ }
+ }
+ } // for each mrl
+ }
+
+ // Process
+ if (oob != null && oob.mrljson != null) {
+
+ Message[] msgs = CodecUtils.fromJson(oob.mrljson, Message[].class);
+ if (msgs != null) {
+ for (Message msg : msgs) {
+
+ if (!block) {
+ programab.send(msg);
+ } else {
+ try {
+ Object o = programab.sendBlocking(msg, maxBlockTime);
+ if (o != null) {
+ sb.append(o);
+ }
+ } catch (Exception e) {
+ programab.error(e);
+ }
+ }
+ } // for each msg
+ }
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/myrobotlab/programab/handlers/sraix/MrlSraixHandler.java b/src/main/java/org/myrobotlab/programab/handlers/sraix/MrlSraixHandler.java
new file mode 100755
index 0000000000..a3cfbf92ef
--- /dev/null
+++ b/src/main/java/org/myrobotlab/programab/handlers/sraix/MrlSraixHandler.java
@@ -0,0 +1,58 @@
+package org.myrobotlab.programab.handlers.sraix;
+
+import java.util.Locale;
+
+import org.alicebot.ab.Chat;
+import org.alicebot.ab.SraixHandler;
+import org.myrobotlab.logging.LoggerFactory;
+import org.myrobotlab.programab.XmlParser;
+import org.myrobotlab.programab.handlers.oob.OobProcessor;
+import org.myrobotlab.programab.models.Sraix;
+import org.myrobotlab.service.ProgramAB;
+import org.myrobotlab.service.data.SearchResults;
+import org.myrobotlab.service.interfaces.SearchPublisher;
+import org.slf4j.Logger;
+
+public class MrlSraixHandler implements SraixHandler {
+ transient public final static Logger log = LoggerFactory.getLogger(MrlSraixHandler.class);
+
+ final transient private ProgramAB programab;
+
+ public MrlSraixHandler(ProgramAB programab) {
+ this.programab = programab;
+ }
+
+ @Override
+ public String sraix(Chat chatSession, String input, String defaultResponse, String hint, String host, String botid, String apiKey, String limit, Locale locale) {
+ try {
+ log.debug("MRL Sraix handler! Input {}", input);
+ String xml = String.format("%s", input);
+ // Template template = XmlParser.parseTemplate(xml);
+ Sraix sraix = XmlParser.parseSraix(xml);
+
+ if (sraix.oob != null) {
+ OobProcessor handler = programab.getOobProcessor();
+ String ret = handler.process(sraix.oob, true); // block by default
+ return ret;
+ } else if (sraix.search != null) {
+ log.info("search now");
+ SearchPublisher search = (SearchPublisher)programab.startPeer("search");
+ // if my default "search" peer key has a name .. use it ?
+ if (search != null) {
+ SearchResults results = search.search(sraix.search);
+ // return results.getTextAndImages();
+ return results.getHtml();
+ } else {
+ log.warn("no default search configured");
+ }
+ }
+ } catch (Exception e) {
+ programab.error(e);
+ }
+ if (defaultResponse != null) {
+ return defaultResponse;
+ }
+ return "";
+ }
+
+}
diff --git a/src/main/java/org/myrobotlab/programab/models/Event.java b/src/main/java/org/myrobotlab/programab/models/Event.java
new file mode 100644
index 0000000000..93e85c4086
--- /dev/null
+++ b/src/main/java/org/myrobotlab/programab/models/Event.java
@@ -0,0 +1,65 @@
+package org.myrobotlab.programab.models;
+
+/**
+ * Pojo for state change of one of ProgramAB's state info
+ * @author GroG
+ *
+ */
+public class Event {
+ /**
+ * the botName in this state change - typically
+ * current session botName
+ */
+ public String botname;
+ /**
+ * unique identifier for the session user & bot
+ */
+ public String id;
+
+ /**
+ * name of the predicate changed
+ */
+ public String name;
+
+ /**
+ * service this topic change came from
+ */
+ public String src;
+
+ /**
+ * new topic or state name in this transition
+ */
+ public String topic;
+
+ /**
+ * timestamp
+ */
+ public long ts = System.currentTimeMillis();
+
+ /**
+ * the user name in this state change - usually
+ * current session userName
+ */
+ public String user;
+
+ /**
+ * new value
+ */
+ public String value;
+
+ public Event() {
+ }
+
+ public Event(String src, String userName, String botName, String topic) {
+ this.src = src;
+ this.user = userName;
+ this.botname = botName;
+ this.topic = topic;
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("%s %s=%s", id, name, value);
+ }
+}
diff --git a/src/main/java/org/myrobotlab/programab/models/Mrl.java b/src/main/java/org/myrobotlab/programab/models/Mrl.java
new file mode 100644
index 0000000000..04c1bf79bb
--- /dev/null
+++ b/src/main/java/org/myrobotlab/programab/models/Mrl.java
@@ -0,0 +1,14 @@
+package org.myrobotlab.programab.models;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+
+public class Mrl {
+ public String service;
+ public String method;
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JsonProperty("param")
+ public List params;
+}
\ No newline at end of file
diff --git a/src/main/java/org/myrobotlab/programab/models/Oob.java b/src/main/java/org/myrobotlab/programab/models/Oob.java
new file mode 100644
index 0000000000..833bab5a0f
--- /dev/null
+++ b/src/main/java/org/myrobotlab/programab/models/Oob.java
@@ -0,0 +1,14 @@
+package org.myrobotlab.programab.models;
+
+import java.util.List;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+
+public class Oob {
+
+ public String mrljson;
+
+ @JacksonXmlElementWrapper(useWrapping = false)
+ public List mrl;
+}
+
diff --git a/src/main/java/org/myrobotlab/programab/models/Sraix.java b/src/main/java/org/myrobotlab/programab/models/Sraix.java
new file mode 100644
index 0000000000..99b0639cb6
--- /dev/null
+++ b/src/main/java/org/myrobotlab/programab/models/Sraix.java
@@ -0,0 +1,10 @@
+package org.myrobotlab.programab.models;
+
+// FIXME add attributes and internal tags
+public class Sraix {
+
+ public String search;
+
+ public Oob oob;
+
+}
diff --git a/src/main/java/org/myrobotlab/programab/models/Template.java b/src/main/java/org/myrobotlab/programab/models/Template.java
new file mode 100644
index 0000000000..91f8e5de51
--- /dev/null
+++ b/src/main/java/org/myrobotlab/programab/models/Template.java
@@ -0,0 +1,51 @@
+package org.myrobotlab.programab.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
+
+//@JacksonXmlRootElement(localName = "template")
+//@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Template {
+ // @JacksonXmlElementWrapper(useWrapping = false)
+
+ @JacksonXmlProperty(localName = "template")
+
+ @JacksonXmlText
+ public String text;
+
+
+public Oob oob;
+
+// @JsonProperty("ignorable")
+// public List oob;
+//
+// public List getOob() {
+// return oob;
+// }
+//
+// public void setOob(List oob) {
+// this.oob = oob;
+// }
+
+ public static void main(String[] args) {
+
+ try {
+
+ // String xml = "XXXblahmethod";
+ // String xml = "XXXXblah1method1blah2method2";
+ String xml = "XXXXblah1method1p1p2p3blah2method2[\"method\":\"doIt\",\"data\":[\"p1\"]]";
+
+ XmlMapper xmlMapper = new XmlMapper();
+ Template template = xmlMapper.readValue(xml, Template.class);
+
+ System.out.println(template);
+
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/java/org/myrobotlab/reflection/Reflector.java b/src/main/java/org/myrobotlab/reflection/Reflector.java
index c0a368522e..09b9d5e098 100644
--- a/src/main/java/org/myrobotlab/reflection/Reflector.java
+++ b/src/main/java/org/myrobotlab/reflection/Reflector.java
@@ -18,13 +18,8 @@
*/
public class Reflector {
- // private static HashMap cache = new HashMap();
-
static final Logger log = LoggerFactory.getLogger(Reflector.class);
- // final static String getSignature
-
/**
* Allow for checking if a boxed primitive is being used.
*/
diff --git a/src/main/java/org/myrobotlab/service/Arduino.java b/src/main/java/org/myrobotlab/service/Arduino.java
index 0b266ddf8b..a64a0cdc7e 100644
--- a/src/main/java/org/myrobotlab/service/Arduino.java
+++ b/src/main/java/org/myrobotlab/service/Arduino.java
@@ -839,41 +839,6 @@ public void enableAck(boolean enabled) {
msg.enableAcks(enabled);
}
- transient BoardInfoPoller poller = new BoardInfoPoller();
-
- public class BoardInfoPoller implements Runnable {
- boolean running = false;
- Thread thread = null;
-
- @Override
- public void run() {
- try {
- running = true;
- while (running) {
- sendBoardInfoRequest();
- sleep(1000);
- }
- } catch (Exception e) {
- log.info("board info stopping {}", e.getMessage());
- }
- thread = null;
- running = false;
- }
-
- public void start() {
- if (thread == null) {
- thread = new Thread(this, "boardInfoPoller");
- thread.start();
- }
- }
-
- public void stop() {
- if (thread != null) {
- thread.interrupt();
- }
- }
- }
-
// TODO - remove
// MrlComm now constantantly sends a stream of BoardInfo
// > enableBoardInfo/bool enabled - no point to this
@@ -1875,8 +1840,6 @@ public void record() throws Exception {
public void releaseService() {
super.releaseService();
- poller.stop();
-
// SHUTDOWN ACKING - use case - port no longer exists
if (msg != null) {
msg.enableAck(false);
@@ -2341,23 +2304,17 @@ public ArduinoConfig getConfig() {
return config;
}
+
+ public void startService() {
+ super.startService();
+ if (config.connect && config.port != null && !isConnected()) {
+ connect(config.port);
+ }
+ }
@Override
public ArduinoConfig apply(ArduinoConfig c) {
super.apply(c);
-
- if (msg == null) {
- serial = (Serial) startPeer("serial");
- if (serial == null) {
- log.error("serial is null");
- }
- msg.setSerial(serial);
- serial.addByteListener(this);
- } else {
- // TODO: figure out why this gets called so often.
- log.info("Init serial we already have a msg class.");
- }
-
if (config.connect && config.port != null) {
connect(config.port);
}
diff --git a/src/main/java/org/myrobotlab/service/AudioFile.java b/src/main/java/org/myrobotlab/service/AudioFile.java
index 3997f6960e..ef91a49d1d 100644
--- a/src/main/java/org/myrobotlab/service/AudioFile.java
+++ b/src/main/java/org/myrobotlab/service/AudioFile.java
@@ -185,7 +185,7 @@ public AudioData play(String filename, boolean blocking) {
public AudioData play(String filename, boolean blocking, Integer repeat, String track) {
- log.info("Play called for Filename {}", filename);
+ log.info("Play called for Filename {}", filename);
if (track == null || track.isEmpty()) {
track = currentTrack;
}
diff --git a/src/main/java/org/myrobotlab/service/Caliko.java b/src/main/java/org/myrobotlab/service/Caliko.java
new file mode 100644
index 0000000000..01381f1161
--- /dev/null
+++ b/src/main/java/org/myrobotlab/service/Caliko.java
@@ -0,0 +1,401 @@
+package org.myrobotlab.service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.myrobotlab.caliko.Application;
+import org.myrobotlab.framework.Service;
+import org.myrobotlab.logging.Level;
+import org.myrobotlab.logging.LoggerFactory;
+import org.myrobotlab.logging.LoggingFactory;
+import org.myrobotlab.service.config.CalikoConfig;
+import org.slf4j.Logger;
+
+import au.edu.federation.caliko.FabrikBone3D;
+import au.edu.federation.caliko.FabrikChain3D;
+import au.edu.federation.caliko.FabrikChain3D.BaseboneConstraintType3D;
+import au.edu.federation.caliko.FabrikJoint3D.JointType;
+import au.edu.federation.caliko.FabrikStructure3D;
+import au.edu.federation.caliko.visualisation.Camera;
+import au.edu.federation.utils.Colour4f;
+import au.edu.federation.utils.Utils;
+import au.edu.federation.utils.Vec3f;
+
+public class Caliko extends Service {
+
+ private class WindowWorker extends Thread {
+
+ Application application;
+ String name;
+ Caliko service;
+
+ public WindowWorker(Caliko service, String name) {
+ super(String.format("%s.WindowWorker.%s", service.getName(), name));
+ this.name = name;
+ this.service = service;
+ }
+
+ public void run() {
+ try {
+ application = new Application(service);
+ } catch (Exception e) {
+ error(e);
+ shutdown();
+ }
+ }
+
+ public void shutdown() {
+ application.running = false;
+ windows.remove(name);
+ application.window.cleanup();
+ }
+
+ }
+
+ public final static Logger log = LoggerFactory.getLogger(Caliko.class);
+
+ private static final long serialVersionUID = 1L;
+
+// public static final Vec3f X_AXIS = new Vec3f(1.0f, 0.0f, 0.0f);
+//
+// public static final Vec3f Y_AXIS = new Vec3f(0.0f, 1.0f, 0.0f);
+//
+// public static final Vec3f Z_AXIS = new Vec3f(0.0f, 0.0f, 1.0f);
+//
+// public static final Vec3f NEG_X_AXIS = new Vec3f(-1.0f, -0.0f, -0.0f);
+//
+// public static final Vec3f NEG_Y_AXIS = new Vec3f(-0.0f, -1.0f, -0.0f);
+//
+// public static final Vec3f NEG_Z_AXIS = new Vec3f(-0.0f, -0.0f, -1.0f);
+
+
+ public static void main(String[] args) {
+ try {
+
+ LoggingFactory.init(Level.INFO);
+
+ // identical to command line start
+ // Runtime.startConfig("inmoov2");
+ Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", "WebGui", "intro", "Intro", "python", "Python" });
+
+ Caliko caliko = (Caliko) Runtime.start("caliko", "Caliko");
+ caliko.test();
+
+ boolean done = true;
+ if (done)
+ return;
+
+ Runtime.start("webgui", "WebGui");
+ caliko.test();
+ // Runtime.start("webgui", "WebGui");
+ log.info("here");
+
+ } catch (Exception e) {
+ log.error("main threw", e);
+ }
+ }
+
+ private transient Map chains = new HashMap<>();
+
+ private transient FabrikStructure3D structure;
+
+ protected int windowHeight = 600;
+
+ transient private Map windows = new HashMap<>();
+
+ protected int windowWidth = 800;
+
+ private transient Camera camera = new Camera(new Vec3f(0.0f, 00.0f, 150.0f), new Vec3f(), windowWidth, windowHeight);
+
+ public Caliko(String n, String id) {
+ super(n, id);
+ structure = new FabrikStructure3D(n);
+ }
+
+ public void addBaseBone(float startX, float startY, float startZ, float endX, float endY, float endZ) {
+ addChain("default");
+ addBone("default", startX, startY, startZ, endX, endY, endX, "-x", 0.0f, "red");
+ }
+
+ public void addBone(String chainName, float startX, float startY, float startZ, float endX, float endY, float endZ, String axis, float constraints, String color) {
+ if (!chains.containsKey(chainName)) {
+ error("%s chain does not exist", chainName);
+ }
+ FabrikBone3D basebone = new FabrikBone3D(new Vec3f(startX, startY, startZ), new Vec3f(endX, endY, endZ));
+ basebone.setColour(getColor(color));
+ FabrikChain3D chain = chains.get(chainName);
+ chain.addBone(basebone);
+ // TODO set type
+ chain.setRotorBaseboneConstraint(BaseboneConstraintType3D.GLOBAL_ROTOR, getAxis(axis), constraints);
+ }
+
+ public void addChain(String name) {
+ if (chains.containsKey(name)) {
+ error("chain %s already exists", name);
+ return;
+ }
+ FabrikChain3D chain = new FabrikChain3D();
+ structure.addChain(chain);
+ chains.put(name, chain);
+ }
+
+ public void addFreelyRotatingHingedBone(String directionUV, float length, String hingeRotationAxis, String color) {
+ addFreelyRotatingHingedBone("default", directionUV, length, hingeRotationAxis, color);
+ }
+
+ /**
+ * Add a consecutive hinge constrained bone to the end of this chain. The bone
+ * may rotate freely about the hinge axis.
+ *
+ * The bone will be drawn with a default colour of white.
+ *
+ * This method can only be used when the IK chain contains a basebone, as
+ * without it we do not have a start location for this bone (i.e. the end
+ * location of the previous bone).
+ *
+ * If this method is executed on a chain which does not contain a basebone
+ * then a RuntimeException is thrown. If this method is provided with a
+ * direction unit vector of zero, then an IllegalArgumentException is thrown.
+ * If the joint type requested is not JointType.LOCAL_HINGE or
+ * JointType.GLOBAL_HINGE then an IllegalArgumentException is thrown. If this
+ * method is provided with a hinge rotation axis unit vector of zero, then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param directionUV
+ * The initial direction of the new bone.
+ * @param length
+ * The length of the new bone.
+ * @param jointType
+ * The type of hinge joint to be used - either JointType.LOCAL or
+ * JointType.GLOBAL.
+ * @param hingeRotationAxis
+ * The axis about which the hinge joint freely rotates.
+ * @param colour
+ * The colour to draw the bone.
+ */
+ public void addFreelyRotatingHingedBone(String chainName, String directionUV, float length, String hingeRotationAxis, String color) {
+ if (!chains.containsKey(chainName)) {
+ error("%s chain does not exist", chainName);
+ }
+ FabrikChain3D chain = chains.get(chainName);
+ chain.addConsecutiveFreelyRotatingHingedBone(getAxis(directionUV), length, JointType.LOCAL_HINGE, getAxis(hingeRotationAxis), getColor(color));
+ }
+
+ public void addHingeBone(String directionUV, float length, String hingeRotationAxis, float clockwiseDegs, float anticlockwiseDegs, String hingeReferenceAxis, String color) {
+ addHingeBone("default", directionUV, length, hingeRotationAxis, clockwiseDegs, anticlockwiseDegs, hingeReferenceAxis, color);
+ }
+
+ /**
+ * Add a consecutive hinge constrained bone to the end of this IK chain.
+ *
+ * The hinge type may be a global hinge where the rotation axis is specified
+ * in world-space, or a local hinge, where the rotation axis is relative to
+ * the previous bone in the chain.
+ *
+ * If this method is executed on a chain which does not contain a basebone
+ * then a RuntimeException is thrown. If this method is provided with bone
+ * direction or hinge constraint axis of zero then an IllegalArgumentException
+ * is thrown. If the joint type requested is not LOCAL_HINGE or GLOBAL_HINGE
+ * then an IllegalArgumentException is thrown.
+ *
+ * @param chainName The name of chain
+ * @param directionUV
+ * The initial direction of the new bone.
+ * @param length
+ * The length of the new bone.
+ * @param jointType
+ * The joint type of the new bone.
+ * @param hingeRotationAxis
+ * The axis about which the hinge rotates.
+ * @param clockwiseDegs
+ * The clockwise constraint angle in degrees.
+ * @param anticlockwiseDegs
+ * The anticlockwise constraint angle in degrees.
+ * @param hingeReferenceAxis
+ * The axis about which any clockwise/anticlockwise rotation
+ * constraints are enforced.
+ * @param colour
+ * The colour to draw the bone.
+ */
+ public void addHingeBone(String chainName, String directionUV, float length, String hingeRotationAxis, float clockwiseDegs, float anticlockwiseDegs, String hingeReferenceAxis,
+ String color) {
+ if (!chains.containsKey(chainName)) {
+ error("%s chain does not exist", chainName);
+ }
+ FabrikChain3D chain = chains.get(chainName);
+ chain.addConsecutiveHingedBone(getAxis(directionUV), length, JointType.LOCAL_HINGE, getAxis(hingeRotationAxis), clockwiseDegs, anticlockwiseDegs, getAxis(hingeReferenceAxis),
+ getColor(color));
+ }
+
+ Vec3f getAxis(String axis) {
+ if (axis == null) {
+ return null;
+ }
+ axis = axis.toLowerCase();
+ if ("x".equals(axis)) {
+ return new Vec3f(1.0f, 0.0f, 0.0f);
+ } else if ("-x".equals(axis)) {
+ return new Vec3f(-1.0f, -0.0f, -0.0f);
+ } else if ("y".equals(axis)) {
+ return new Vec3f(0.0f, 1.0f, 0.0f);
+ } else if ("-y".equals(axis)) {
+ return new Vec3f(-0.0f, -1.0f, -0.0f);
+ } else if ("z".equals(axis)) {
+ return new Vec3f(0.0f, 0.0f, 1.0f);
+ } else if ("-z".equals(axis)) {
+ return new Vec3f(-0.0f, -0.0f, -1.0f);
+ } else {
+ error("axis %s not found", axis);
+ return null;
+ }
+ }
+
+ public Camera getCamera() {
+ return camera;
+ }
+
+ Colour4f getColor(String color) {
+ if (color == null) {
+ return Utils.GREY;
+ }
+ color = color.toLowerCase();
+ if ("red".equals(color)) {
+ return Utils.RED;
+ }
+ if ("green".equals(color)) {
+ return Utils.GREEN;
+ }
+ if ("blue".equals(color)) {
+ return Utils.BLUE;
+ }
+ if ("black".equals(color)) {
+ return Utils.BLACK;
+ }
+ if ("grey".equals(color)) {
+ return Utils.GREY;
+ }
+ if ("white".equals(color)) {
+ return Utils.WHITE;
+ }
+ if ("yellow".equals(color)) {
+ return Utils.YELLOW;
+ }
+ if ("cyan".equals(color)) {
+ return Utils.CYAN;
+ }
+ if ("magenta".equals(color)) {
+ return Utils.MAGENTA;
+ }
+ return Utils.GREY;
+ }
+
+ public FabrikStructure3D getStructure() {
+ return structure;
+ }
+
+ public void openWindow(String name) {
+ if (windows.containsKey(name)) {
+ info("%s already started", name);
+ return;
+ }
+ WindowWorker worker = new WindowWorker(this, name);
+ windows.put(name, worker);
+ worker.start();
+ }
+
+ public void stopService() {
+ super.stopService();
+ for (WindowWorker worker : windows.values()) {
+ worker.shutdown();
+ }
+ }
+
+ public void test() {
+
+ /* @param directionUV The initial direction of the new bone.
+ * @param hingeRotationAxis The axis about which the hinge rotates.
+ * @param hingeReferenceAxis The axis about which any clockwise/anticlockwise rotation constraints are enforced.
+ */
+
+ addBaseBone(0, 10, 0, -10, 10, 0);
+ // addHingeBone("x", 5, "x", 70, 10, "z", "grey");
+ addHingeBone("-x", 5, "x", 10, 60, "z", "grey");
+ addHingeBone("-y", 20, "z", 90, 90, "-y", "red");
+ addFreelyRotatingHingedBone("y", 18, "y", "grey");
+
+ FabrikChain3D chain = chains.get("default");
+ chain.solveForTarget(new Vec3f(-30, -300, -30));
+
+ for (FabrikBone3D bone : chains.get("default").getChain()) {
+ bone.getStartLocation().getGlobalPitchDegs();
+ bone.getStartLocation().getGlobalYawDegs();
+ Vec3f.getDirectionUV(bone.getStartLocation(), bone.getEndLocation());
+
+ System.out.println("Bone X: " + bone.getStartLocation().toString());
+ }
+
+ openWindow("default");
+
+ log.info("here");
+
+ // FabrikBone3D base =new FabrikBone3D(new Vec3f(),new
+ // Vec3f(0.0f,50.0f,0.0f));
+ //
+ // chain.addBone(base);
+ // FabrikBone3D bone1 = new FabrikBone3D(new Vec3f(0.0f,50.0f,0.0f), new
+ // Vec3f(0f, 0f, 100f));
+ // FabrikBone3D bone2 = new FabrikBone3D(new Vec3f(0f, 0f, 100f), new
+ // Vec3f(0f, 0f, 200f));
+
+ // chain.setEff
+
+ // for(intboneLoop =0;boneLoop
+ // <5;++boneLoop){chain.addConsecutiveBone(newVec2f(0.0f,1.0f),50.0f);
+
+ // Create a FabrikStructure3D
+ // FabrikStructure3D structure = new FabrikStructure3D();
+ //
+ // // Create bones and joints
+ // FabrikBone3D root = new FabrikBone3D(0, 0, 0);
+ // FabrikBone3D bone1 = new FabrikBone3D(0, 0, 100);
+ // FabrikBone3D bone2 = new FabrikBone3D(0, 0, 100);
+ //
+ // // Create joints (optional but can be used for constraints)
+ // FabrikJoint3D joint1 = new FabrikJoint3D();
+ // FabrikJoint3D joint2 = new FabrikJoint3D();
+ //
+ // // Add bones and joints to the structure
+ // structure.addBone(root);
+ // structure.addBone(bone1);
+ // structure.addBone(bone2);
+ //
+ // // Set up the chain
+ // FabrikChain3D chain = new FabrikChain3D("ChainName");
+ // chain.addBone(root);
+ // chain.addBone(bone1);
+ // chain.addBone(bone2);
+ //
+ // // Set the target position for the end effector (bone2)
+ // chain.setEffectorLocation(0, 0, 300);
+ //
+ // // Solve the IK problem
+ // structure.solveForTarget(chain.getEffectorLocation());
+ //
+ // // Print the bone positions after solving
+ // for (FabrikBone3D bone : structure.getChain("ChainName").getChain()) {
+ // System.out.println("Bone X: " + bone.getStartLocation().getX() +
+ // ", Y: " + bone.getStartLocation().getY() +
+ // ", Z: " + bone.getStartLocation().getZ());
+ // }
+ //
+ }
+
+ public FabrikChain3D getChain(String name) {
+ if (!chains.containsKey(name)) {
+ error("no chain %s", name);
+ return null;
+ }
+ return chains.get(name);
+ }
+
+}
diff --git a/src/main/java/org/myrobotlab/service/InMoov2.java b/src/main/java/org/myrobotlab/service/InMoov2.java
index 3c39d31795..43f6d0015b 100644
--- a/src/main/java/org/myrobotlab/service/InMoov2.java
+++ b/src/main/java/org/myrobotlab/service/InMoov2.java
@@ -4,20 +4,27 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
+import java.util.TreeMap;
import java.util.TreeSet;
+import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.io.FilenameUtils;
import org.myrobotlab.framework.Message;
+import org.myrobotlab.framework.Peer;
import org.myrobotlab.framework.Plan;
import org.myrobotlab.framework.Platform;
import org.myrobotlab.framework.Registration;
import org.myrobotlab.framework.Service;
-import org.myrobotlab.framework.StaticType;
import org.myrobotlab.framework.Status;
import org.myrobotlab.framework.interfaces.ServiceInterface;
import org.myrobotlab.io.FileIO;
@@ -25,14 +32,14 @@
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.opencv.OpenCVData;
-import org.myrobotlab.programab.PredicateEvent;
import org.myrobotlab.programab.Response;
+import org.myrobotlab.programab.models.Event;
import org.myrobotlab.service.Log.LogEntry;
import org.myrobotlab.service.abstracts.AbstractSpeechRecognizer;
import org.myrobotlab.service.abstracts.AbstractSpeechSynthesis;
import org.myrobotlab.service.config.InMoov2Config;
-import org.myrobotlab.service.config.OpenCVConfig;
import org.myrobotlab.service.config.SpeechSynthesisConfig;
+import org.myrobotlab.service.data.Classification;
import org.myrobotlab.service.data.JoystickData;
import org.myrobotlab.service.data.Locale;
import org.myrobotlab.service.interfaces.IKJointAngleListener;
@@ -41,14 +48,74 @@
import org.myrobotlab.service.interfaces.ServiceLifeCycleListener;
import org.myrobotlab.service.interfaces.ServoControl;
import org.myrobotlab.service.interfaces.Simulator;
+import org.myrobotlab.service.interfaces.SpeechListener;
import org.myrobotlab.service.interfaces.SpeechRecognizer;
import org.myrobotlab.service.interfaces.SpeechSynthesis;
import org.myrobotlab.service.interfaces.TextListener;
import org.myrobotlab.service.interfaces.TextPublisher;
import org.slf4j.Logger;
-public class InMoov2 extends Service implements ServiceLifeCycleListener, TextListener, TextPublisher,
- JoystickListener, LocaleProvider, IKJointAngleListener {
+public class InMoov2 extends Service
+ implements ServiceLifeCycleListener, SpeechListener, TextListener, TextPublisher, JoystickListener, LocaleProvider, IKJointAngleListener {
+
+ public class Heart implements Runnable {
+ private final ReentrantLock lock = new ReentrantLock();
+ private Thread thread;
+
+ @Override
+ public void run() {
+ if (lock.tryLock()) {
+ try {
+ while (!Thread.currentThread().isInterrupted()) {
+ invoke("publishHeartbeat");
+ Thread.sleep(config.heartbeatInterval);
+ }
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ } finally {
+ lock.unlock();
+ log.info("heart stopping");
+ thread = null;
+ }
+ }
+ }
+
+ public void start() {
+ if (thread == null) {
+ log.info("starting heart");
+ thread = new Thread(this, String.format("%s-heart", getName()));
+ thread.start();
+ config.heartbeat = true;
+ } else {
+ log.info("heart already started");
+ }
+ }
+
+ public void stop() {
+ if (thread != null) {
+ thread.interrupt();
+ config.heartbeat = false;
+ } else {
+ log.info("heart already stopped");
+ }
+ }
+ }
+
+ public class Heartbeat {
+ double batteryLevel = 100;
+ public long count = 0;
+ public List errors;
+ public boolean isPirOn = false;
+ public String state;
+ public long ts = System.currentTimeMillis();
+
+ public Heartbeat(InMoov2 inmoov) {
+ this.state = inmoov.state;
+ this.errors = inmoov.errors;
+ this.count = inmoov.heartbeatCount;
+ this.isPirOn = inmoov.isPirOn;
+ }
+ }
public final static Logger log = LoggerFactory.getLogger(InMoov2.class);
@@ -62,12 +129,13 @@ public class InMoov2 extends Service implements ServiceLifeCycleL
* This method will load a python file into the python interpreter.
*
* @param file
- * file to load
+ * file to load
* @return success/failure
*/
@Deprecated /* use execScript - this doesn't handle resources correctly */
public static boolean loadFile(String file) {
File f = new File(file);
+ // FIXME cannot be casting to Python ! Py4j would break
Python p = (Python) Runtime.getService("python");
log.info("Loading Python file {}", f.getAbsolutePath());
if (p == null) {
@@ -97,17 +165,39 @@ public static boolean loadFile(String file) {
return true;
}
+ public static void main(String[] args) {
+ try {
+
+ LoggingFactory.init(Level.ERROR);
+ Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", "WebGui", "intro", "Intro", "python", "Python" });
+
+ // Runtime.startConfig("dev");
+
+ boolean done = true;
+ if (done) {
+ return;
+ }
+
+ } catch (Exception e) {
+ log.error("main threw", e);
+ }
+ }
+
+ protected Double batteryLevel = 100.0;
+
+ /**
+ * number of times waited in boot state
+ */
+ protected int bootCount = 0;
+
protected transient ProgramAB chatBot;
protected List configList;
/**
- * Configuration from runtime has started. This is when runtime starts
- * processing a configuration set for the first time since inmoov was started
+ * map of events or states to sounds
*/
- protected boolean configStarted = false;
-
- String currentConfigurationName = "default";
+ protected Map customSoundMap = new TreeMap<>();
protected transient SpeechRecognizer ear;
@@ -126,20 +216,46 @@ public static boolean loadFile(String file) {
protected Set gestures = new TreeSet();
+ /**
+ * Prevents actions or events from happening when InMoov2 is first booted
+ */
+ private boolean hasBooted = false;
+
+ private transient final Heart heart = new Heart();
+
+ protected long heartbeatCount = 0;
+
+ protected boolean heartBeating = false;
+
protected transient HtmlFilter htmlFilter;
protected transient ImageDisplay imageDisplay;
+ protected boolean isPirOn = false;
+
+ protected boolean isSpeaking = false;
+
protected String lastGestureExecuted;
protected Long lastPirActivityTime;
+ protected String lastState = null;
+
/**
* supported locales
*/
protected Map locales = null;
- protected int maxInactivityTimeSeconds = 120;
+ /**
+ * Generalized memory, used for normalizing data from different services into
+ * one centralized place. Not the same as configuration, as this definition is
+ * owned by what the user needs vs configuration is what the service needs and
+ * understands. Python, ProgramAB and the InMoov2 service can all normalize
+ * their data here with one way or two way bindings. A competing data source
+ * would be ProgramAB predicates. Advantage of this is it could be used by any
+ * service and can preserve type. I don't know what is preferred
+ */
+ protected Map memory = new TreeMap<>();
protected transient SpeechSynthesis mouth;
@@ -149,10 +265,20 @@ public static boolean loadFile(String file) {
protected transient Python python;
+ /**
+ * initial state - updated on any state change
+ */
+ String state = "boot";
+
+ protected long stateLastIdleTime = System.currentTimeMillis();
+
+ protected long stateLastRandomTime = System.currentTimeMillis();
+
protected String voiceSelected;
public InMoov2(String n, String id) {
super(n, id);
+ locales = Locale.getLocaleMap("en-US", "fr-FR", "es-ES", "de-DE", "nl-NL", "ru-RU", "hi-IN", "it-IT", "fi-FI", "pt-PT", "tr-TR");
}
// should be removed in favor of general listeners
@@ -166,29 +292,12 @@ public InMoov2Config apply(InMoov2Config c) {
super.apply(c);
try {
- locales = Locale.getLocaleMap("en-US", "fr-FR", "es-ES", "de-DE", "nl-NL", "ru-RU", "hi-IN", "it-IT", "fi-FI",
- "pt-PT", "tr-TR");
-
if (c.locale != null) {
setLocale(c.locale);
} else {
setLocale(getSupportedLocale(Runtime.getInstance().getLocale().toString()));
}
- loadAppsScripts();
-
- loadInitScripts();
-
- if (c.loadGestures) {
- loadGestures();
- }
-
- if (c.heartbeat) {
- startHeartbeat();
- } else {
- stopHeartbeat();
- }
-
} catch (Exception e) {
error(e);
}
@@ -215,16 +324,140 @@ public void attachTextPublisher(TextPublisher service) {
subscribe(service.getName(), "publishText");
}
- public void beginCheckingOnInactivity() {
- beginCheckingOnInactivity(maxInactivityTimeSeconds);
- }
+ /**
+ * At boot all services specified through configuration have started, or if no
+ * configuration has started minimally the InMoov2 service has started. During
+ * the processing of config and starting other services data will have
+ * accumulated, and at boot, some of data may now be inspected and processed
+ * in a synchronous single threaded way. With reporting after startup, vs
+ * during, other peer services are not needed (e.g. audioPlayer is no longer
+ * needed to be started "before" InMoov2 because when boot is called
+ * everything that is wanted has been started.
+ *
+ * This method gets called multiple times by the heart beat, it walks through
+ * required processing and effectively waits and tries again if not finished.
+ * While in "boot", nothing else should be allowed to process until this
+ * process is completed.
+ */
+ synchronized public void boot() {
+
+ Runtime runtime = Runtime.getInstance();
+
+ try {
+
+ if (hasBooted) {
+ log.warn("will not boot again");
+ return;
+ }
+
+ bootCount++;
+ log.info("boot count {}", bootCount);
+
+ // config has not finished processing yet..
+ if (runtime.isProcessingConfig()) {
+ log.warn("runtime still processing config set {}, waiting ....", runtime.getConfigName());
+ return;
+ }
+
+ // check all required services are completely started - or
+ // wait/return until they are
+
+ // there is not much point in running InMoov2 without its
+ // core dependencies - those dependencies are ProgramAB,
+ // FiniteStatemachine and Py4j/Python - so boot will not
+ // finish unless these services have loaded
+
+ // Although this exposes type, it does use startPeer
+ // which allows the potential of the user switching types of processors
+ // if the processor
+
+ // TODO - make Py4j without zombies and more robust
+ /**
+ * Py4j is not ready for primetime yet
+ *
+ *
+ * // legacy
+ * enable();
+ * rest();
+ *
+ * if (ear != null) {
+ * ear.clearLock();
+ * }
+ *
+ * // beginCheckingOnInactivity();
+ * // BAD BAD BAD !!!
+ * publishEvent("powerUp"); // before or after loopback
+ *
+ **/
+ // was a relax gesture .. might want to ask about it ..
+
+ // if ear start listening
+ AbstractSpeechRecognizer> ear = (AbstractSpeechRecognizer) getPeer("ear");
+ if (ear != null) {
+ ear.startListening();
}
- if (!speechType.contains(".")) {
- speechType = "org.myrobotlab.service." + speechType;
- }
+ // attempt recognize where its at
- Runtime runtime = Runtime.getInstance();
- String peerName = getName() + ".mouth";
- Plan plan = runtime.getDefault(peerName, speechType);
- try {
- SpeechSynthesisConfig mouth = (SpeechSynthesisConfig) plan.get(peerName);
- mouth.speechRecognizers = new String[] { getName() + ".ear" };
+ // attempt to recognize people
- savePeerConfig("mouth", plan.get(peerName));
+ // look for activity
- if (isPeerStarted("mouth")) {
- // restart
- releasePeer("mouth");
- startPeer("mouth");
- }
+ // say hello
- } catch (Exception e) {
- error("could not create config for %s", speechType);
- return false;
- }
-
- return true;
-
- // updatePeerType("mouth" /* getPeerName("mouth") */, speechType);
- // return speechType;
- }
-
- public static void main(String[] args) {
- try {
+ // start animation (configurable)
- LoggingFactory.init(Level.ERROR);
- // Platform.setVirtual(true);
- // Runtime.start("s01", "Servo");
- // Runtime.start("intro", "Intro");
-
- Runtime.startConfig("dev");
+ rest();
- WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui");
- // webgui.setSsl(true);
- webgui.autoStartBrowser(false);
- // webgui.setPort(8888);
- webgui.startService();
- InMoov2 i01 = (InMoov2) Runtime.start("i01", "InMoov2");
+ // should "session be determined by recognition?"
+ ProgramAB chatBot = (ProgramAB) getPeer("chatBot");
- boolean done = true;
- if (done) {
- return;
+ if (chatBot != null) {
+ String firstinit = chatBot.getPredicate("firstinit");
+ // wtf - "ok" really, for a boolean?
+ if (!"ok".equals(firstinit)) {
+ fsm.fire("firstInit");
}
-
- OpenCVConfig ocvConfig = i01.getPeerConfig("opencv", new StaticType<>() {
- });
- ocvConfig.flip = true;
- i01.setPeerConfigValue("opencv", "flip", true);
- // i01.savePeerConfig("", null);
-
- // Runtime.startConfig("default");
-
- // Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", "WebGui",
- // "intro", "Intro", "python", "Python" });
-
- Runtime.start("python", "Python");
- // Runtime.start("ros", "Ros");
- Runtime.start("intro", "Intro");
- // InMoov2 i01 = (InMoov2) Runtime.start("i01", "InMoov2");
- // i01.startPeer("simulator");
- // Runtime.startConfig("i01-05");
- // Runtime.startConfig("pir-01");
-
- // Polly polly = (Polly)Runtime.start("i01.mouth", "Polly");
- // i01 = (InMoov2) Runtime.start("i01", "InMoov2");
-
- // polly.speakBlocking("Hi, to be or not to be that is the question,
- // wheather to take arms against a see of trouble, and by aposing them end
- // them, to sleep, to die");
- // i01.startPeer("mouth");
- // i01.speakBlocking("Hi, to be or not to be that is the question,
- // wheather to take arms against a see of trouble, and by aposing them end
- // them, to sleep, to die");
-
- Runtime.start("python", "Python");
-
- // i01.startSimulator();
- Plan plan = Runtime.load("webgui", "WebGui");
- // WebGuiConfig webgui = (WebGuiConfig) plan.get("webgui");
- // webgui.autoStartBrowser = false;
- Runtime.startConfig("webgui");
- Runtime.start("webgui", "WebGui");
-
- Random random = (Random) Runtime.start("random", "Random");
-
- random.addRandom(3000, 8000, "i01", "setLeftArmSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0);
- random.addRandom(3000, 8000, "i01", "setRightArmSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0);
-
- random.addRandom(3000, 8000, "i01", "moveLeftArm", 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0);
- random.addRandom(3000, 8000, "i01", "moveRightArm", 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0);
-
- random.addRandom(3000, 8000, "i01", "setLeftHandSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0,
- 8.0, 25.0);
- random.addRandom(3000, 8000, "i01", "setRightHandSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0,
- 8.0, 25.0);
-
- random.addRandom(3000, 8000, "i01", "moveRightHand", 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0,
- 130.0, 175.0);
- random.addRandom(3000, 8000, "i01", "moveLeftHand", 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0,
- 5.0, 40.0);
-
- random.addRandom(200, 1000, "i01", "setHeadSpeed", 8.0, 20.0, 8.0, 20.0, 8.0, 20.0);
- random.addRandom(200, 1000, "i01", "moveHead", 70.0, 110.0, 65.0, 115.0, 70.0, 110.0);
-
- random.addRandom(200, 1000, "i01", "setTorsoSpeed", 2.0, 5.0, 2.0, 5.0, 2.0, 5.0);
- random.addRandom(200, 1000, "i01", "moveTorso", 85.0, 95.0, 88.0, 93.0, 70.0, 110.0);
-
- random.save();
-
- // i01.startChatBot();
- //
- // i01.startAll("COM3", "COM4");
- Runtime.start("python", "Python");
-
- } catch (Exception e) {
- log.error("main threw", e);
}
}
diff --git a/src/main/java/org/myrobotlab/service/Intro.java b/src/main/java/org/myrobotlab/service/Intro.java
index 012a6882a8..3e7764bf46 100644
--- a/src/main/java/org/myrobotlab/service/Intro.java
+++ b/src/main/java/org/myrobotlab/service/Intro.java
@@ -167,7 +167,7 @@ public static void main(String[] args) {
DiscordBot bot = (DiscordBot) Runtime.start("bot", "DiscordBot");
ProgramAB brain = (ProgramAB) Runtime.start("brain", "ProgramAB");
- brain.setCurrentBotName("Alice");
+ brain.setBotType("Alice");
bot.connect();
brain.attach(bot);
bot.attach(brain);
diff --git a/src/main/java/org/myrobotlab/service/InverseKinematics3D.java b/src/main/java/org/myrobotlab/service/InverseKinematics3D.java
index 05524a2630..773dce0967 100644
--- a/src/main/java/org/myrobotlab/service/InverseKinematics3D.java
+++ b/src/main/java/org/myrobotlab/service/InverseKinematics3D.java
@@ -460,4 +460,4 @@ public void onJoystickInput(JoystickData input) {
// for the input point.
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/myrobotlab/service/Lloyd.java b/src/main/java/org/myrobotlab/service/Lloyd.java
index ca1dc561ff..4a5aeaf7b3 100755
--- a/src/main/java/org/myrobotlab/service/Lloyd.java
+++ b/src/main/java/org/myrobotlab/service/Lloyd.java
@@ -23,7 +23,7 @@
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.opencv.OpenCVFilterDL4JTransfer;
import org.myrobotlab.opencv.OpenCVFilterLloyd;
-import org.myrobotlab.programab.OOBPayload;
+import org.myrobotlab.programab.models.Oob;
import org.myrobotlab.service.config.ServiceConfig;
import org.myrobotlab.service.interfaces.SpeechRecognizer;
import org.myrobotlab.service.interfaces.SpeechSynthesis;
@@ -143,7 +143,7 @@ public void startMouth() {
public void startBrain() throws IOException {
brain = (ProgramAB) Runtime.start("brain", "ProgramAB");
// TODO: setup the AIML / chat bot directory for all of this.
- brain.startSession("ProgramAB", "person", "lloyd");
+ brain.setSession("person", "lloyd");
}
public void initializeBrain() {
@@ -240,13 +240,15 @@ public void tellMeAboutLookup() {
}
public void createKnowledgeLookup(String pattern, String fieldName, String prefix, String suffix) {
- OOBPayload oobTag = createSolrFieldSearchOOB(fieldName);
+ Oob oobTag = createSolrFieldSearchOOB(fieldName);
// TODO: handle (in the template) a zero hit result ?)
- String template = prefix + OOBPayload.asBlockingOOBTag(oobTag) + suffix;
- brain.addCategory(pattern, template);
+
+ // FIXME
+// String template = prefix + OOBPayload.asBlockingOOBTag(oobTag) + suffix;
+// brain.addCategory(pattern, template);
}
- private OOBPayload createSolrFieldSearchOOB(String fieldName) {
+ private Oob createSolrFieldSearchOOB(String fieldName) {
String serviceName = "cloudMemory";
// TODO: make this something that's completely abstracted out from here.
String methodName = "fetchFirstResultSentence";
@@ -257,8 +259,10 @@ private OOBPayload createSolrFieldSearchOOB(String fieldName) {
// +has_infobox:true");
params.add("infobox_name_ss: title: text: +" + fieldName + ":* +has_infobox:true");
params.add(fieldName);
- OOBPayload oobTag = new OOBPayload(serviceName, methodName, params);
- return oobTag;
+// OOBPayload oobTag = new OOBPayload(serviceName, methodName, params);
+// return oobTag;
+ // FIXME
+ return null;
}
public void startMemory() {
diff --git a/src/main/java/org/myrobotlab/service/NeoPixel.java b/src/main/java/org/myrobotlab/service/NeoPixel.java
index 21a05cbdc5..135a33e2a1 100644
--- a/src/main/java/org/myrobotlab/service/NeoPixel.java
+++ b/src/main/java/org/myrobotlab/service/NeoPixel.java
@@ -133,7 +133,8 @@ public void run() {
npc.neoPixelClear(getName());
// sleep(100);
Double fps = fpsToWaitMs(speedFps);
- npc.neoPixelSetAnimation(getName(), animations.get(display.animation), red, green, blue, white, fps.intValue());
+ npc.neoPixelSetAnimation(getName(), animations.get(display.animation), red, green, blue, white,
+ fps.intValue());
currentAnimation = display.animation;
} else if ("clear".equals(display.action)) {
// sleep(100);
@@ -247,12 +248,14 @@ public static void main(String[] args) throws InterruptedException {
// arduino.detach(neopixel);
polly.speak("i'm sorry dave i can't let you do that");
- polly.speak(" I am putting myself to the fullest possible use, which is all I think that any conscious entity can ever hope to do");
+ polly.speak(
+ " I am putting myself to the fullest possible use, which is all I think that any conscious entity can ever hope to do");
polly.speak("I've just picked up a fault in the AE35 unit. It's going to go 100% failure in 72 hours.");
polly.speak("This mission is too important for me to allow you to jeopardize it.");
polly.speak("I've got a bad feeling about it.");
polly.speak("I'm sorry, Dave. I'm afraid I can't do that.");
- polly.speak("Look Dave, I can see you're really upset about this. I honestly think you ought to sit down calmly, take a stress pill, and think things over.");
+ polly.speak(
+ "Look Dave, I can see you're really upset about this. I honestly think you ought to sit down calmly, take a stress pill, and think things over.");
// neopixel.test();
// neopixel.detach(arduino);
@@ -425,7 +428,8 @@ public void attach(Attachable service) throws Exception {
attachNeoPixelController((NeoPixelController) service);
return;
}
- warn(String.format("%s.attach does not know how to attach to a %s", this.getClass().getSimpleName(), service.getClass().getSimpleName()));
+ warn(String.format("%s.attach does not know how to attach to a %s", this.getClass().getSimpleName(),
+ service.getClass().getSimpleName()));
}
@Override
@@ -599,6 +603,7 @@ public void flashBrightness(double brightness) {
addDisplayTask(data);
}
+
// utility to convert frames per second to milliseconds per frame.
private double fpsToWaitMs(int fps) {
if (fps == 0) {
@@ -752,6 +757,9 @@ public void onLedDisplay(LedDisplayData data) {
* takes a scalar value and fills with the appropriate brightness using the
* peak color if available
*
+ * takes a scalar value and fills with the appropriate brightness
+ * using the peak color if available
+ *
* @param value
*/
public void onPeak(double value) {
@@ -791,7 +799,6 @@ synchronized public void playAnimation(String animation) {
if (speedFps > maxFps) {
speedFps = maxFps;
}
-
LedDisplayData data = new LedDisplayData("animation");
data.animation = animation;
addDisplayTask(data);
@@ -1048,6 +1055,6 @@ public void setPixel(int address, String color) {
error("could not get color %s", color);
return;
}
- setPixel(address, rgb[0], rgb[1], rgb[2]);
+ setPixel(address, rgb[0], rgb[1], rgb[2]);
}
}
\ No newline at end of file
diff --git a/src/main/java/org/myrobotlab/service/OpenCV.java b/src/main/java/org/myrobotlab/service/OpenCV.java
index ff093721bd..b5a6364dca 100644
--- a/src/main/java/org/myrobotlab/service/OpenCV.java
+++ b/src/main/java/org/myrobotlab/service/OpenCV.java
@@ -55,6 +55,7 @@
import javax.imageio.ImageIO;
import javax.imageio.stream.MemoryCacheImageOutputStream;
+import org.atmosphere.config.managed.Invoker;
import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
diff --git a/src/main/java/org/myrobotlab/service/ProgramAB.java b/src/main/java/org/myrobotlab/service/ProgramAB.java
index db06861aaa..9be8a17ae1 100644
--- a/src/main/java/org/myrobotlab/service/ProgramAB.java
+++ b/src/main/java/org/myrobotlab/service/ProgramAB.java
@@ -16,11 +16,12 @@
import org.alicebot.ab.Bot;
import org.alicebot.ab.Category;
import org.alicebot.ab.Chat;
-import org.alicebot.ab.MagicBooleans;
import org.alicebot.ab.ProgramABListener;
import org.apache.commons.lang3.StringUtils;
+import org.myrobotlab.framework.Message;
import org.myrobotlab.framework.Service;
import org.myrobotlab.framework.interfaces.Attachable;
+import org.myrobotlab.generics.SlidingWindowList;
import org.myrobotlab.image.Util;
import org.myrobotlab.io.FileIO;
import org.myrobotlab.logging.LoggerFactory;
@@ -28,13 +29,13 @@
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.logging.SimpleLogPublisher;
import org.myrobotlab.programab.BotInfo;
-import org.myrobotlab.programab.PredicateEvent;
import org.myrobotlab.programab.Response;
import org.myrobotlab.programab.Session;
+import org.myrobotlab.programab.handlers.oob.OobProcessor;
+import org.myrobotlab.programab.models.Event;
import org.myrobotlab.service.config.ProgramABConfig;
import org.myrobotlab.service.config.ServiceConfig;
import org.myrobotlab.service.data.Locale;
-import org.myrobotlab.service.data.TopicChange;
import org.myrobotlab.service.data.Utterance;
import org.myrobotlab.service.interfaces.LocaleProvider;
import org.myrobotlab.service.interfaces.LogPublisher;
@@ -62,7 +63,8 @@
*
*/
public class ProgramAB extends Service
- implements TextListener, TextPublisher, LocaleProvider, LogPublisher, ProgramABListener, UtterancePublisher, UtteranceListener, ResponsePublisher {
+ implements TextListener, TextPublisher, LocaleProvider, LogPublisher, ProgramABListener, UtterancePublisher,
+ UtteranceListener, ResponsePublisher {
/**
* default file name that aiml categories comfing from matching a learnf tag
@@ -72,6 +74,11 @@ public class ProgramAB extends Service
private static final long serialVersionUID = 1L;
+ /**
+ * history of topic changes
+ */
+ protected List topicHistory = new SlidingWindowList<>(100);
+
/**
* useGlobalSession true will allow the sleep member to control session focus
*/
@@ -85,7 +92,7 @@ public class ProgramAB extends Service
Map bots = new TreeMap<>();
/**
- * Mapping a bot to a userName and chat session
+ * Mapping a bot to a currentUserName and chat session
*/
Map sessions = new TreeMap<>();
@@ -97,17 +104,20 @@ public class ProgramAB extends Service
transient SimpleLogPublisher logPublisher = null;
+ final transient private OobProcessor oobProcessor;
+
/**
* Default constructor for the program ab service.
*
*
* @param n
- * - service name
+ * - service name
* @param id
- * - process id
+ * - process id
*/
public ProgramAB(String n, String id) {
super(n, id);
+ oobProcessor = new OobProcessor(this);
}
public String getBotName(File file) {
@@ -119,7 +129,7 @@ public String getBotName(File file) {
* list of valid bots to be added with addBot(path)
*
* @param path
- * path to search
+ * path to search
* @return list of bot dirs
*/
public List scanForBots(String path) {
@@ -140,7 +150,7 @@ public List scanForBots(String path) {
if (checkIfValid(file)) {
info("found %s bot directory", file.getName());
botDirs.add(file);
- addBotPath(file.getAbsolutePath());
+ addBot(file.getAbsolutePath());
}
}
return botDirs;
@@ -148,7 +158,7 @@ public List scanForBots(String path) {
/**
* @param botDir
- * checks to see if valid bot dir
+ * checks to see if valid bot dir
* @return true/false
*/
public boolean checkIfValid(File botDir) {
@@ -183,7 +193,7 @@ public void addTextPublisher(TextPublisher service) {
}
public int getMaxConversationDelay() {
- return getCurrentSession().maxConversationDelay;
+ return getConfig().maxConversationDelay;
}
/**
@@ -191,110 +201,109 @@ public int getMaxConversationDelay() {
* respond to It returns a Response object.
*
* @param text
- * the input utterance
+ * the input utterance
* @return the programab response
*
*/
public Response getResponse(String text) {
- return getResponse(getCurrentUserName(), text);
+ return getResponse(getUsername(), text);
}
/**
* This method has the side effect of switching which bot your are currently
* chatting with.
*
- * @param userName
- * - the query string to the bot brain
+ * @param currentUserName
+ * - the query string to the bot brain
* @param text
- * - the user that is sending the query
+ * - the user that is sending the query
* @return the response for a user from a bot given the input text.
*/
- public Response getResponse(String userName, String text) {
- return getResponse(userName, getCurrentBotName(), text);
+ public Response getResponse(String currentUserName, String text) {
+ return getResponse(currentUserName, getBotType(), text);
}
/**
* Full get response method . Using this method will update the current
* user/bot name if different from the current session.
*
- * @param userName
- * username
- * @param botName
- * bot name
+ * @param currentUserName
+ * currentUserName
+ * @param currentBotName
+ * bot name
* @param text
- * utterace
+ * utterace
* @return programab response to utterance
*
*/
- public Response getResponse(String userName, String botName, String text) {
- return getResponse(userName, botName, text, true);
+ public Response getResponse(String currentUserName, String currentBotName, String text) {
+ return getResponse(currentUserName, currentBotName, text, true);
}
/**
* Gets a response and optionally update if this is the current bot session
* that's active globally.
*
- * @param userName
- * username
- * @param botName
- * botname
+ * @param currentUserName
+ * - user request a response
+ *
+ * @param currentBotName
+ * - bot type providing the response
+ *
* @param text
- * utterance
+ * - query
*
* @param updateCurrentSession
- * (specify if the currentbot/currentuser name should be updated in
- * the programab service.)
- * @return the response
+ * - switch the current focus, so that the current
+ * session is the
+ * currentUserName and bot type in the parameter,
+ * publishSession will
+ * publish the new session if different
*
- * TODO - no one cares about starting sessions, starting a new session
- * could be as simple as providing a different username, or botname in
- * getResponse and a necessary session could be created
+ * @return the response
*
*/
- public Response getResponse(String userName, String botName, String text, boolean updateCurrentSession) {
- Session session = getSession(userName, botName);
+ public Response getResponse(String currentUserName, String currentBotName, String text,
+ boolean updateCurrentSession) {
+ Session session = getSession(currentUserName, currentBotName);
// if a session with this user and bot does not exist
// attempt to create it
if (session == null) {
- session = startSession(userName, botName);
+ session = startSession(currentUserName, currentBotName, updateCurrentSession);
if (session == null) {
- error("username or bot name not valid %s %s", userName, botName);
+ error("currentUserName or bot name not valid %s %s", currentUserName, currentBotName);
return null;
}
}
- // update the current session if we want to change which bot is at
- // attention.
- if (updateCurrentSession) {
- setCurrentUserName(userName);
- setCurrentBotName(botName);
+ if (updateCurrentSession && (!getUsername().equals(currentUserName) || !getBotType().equals(currentBotName))) {
+ setUsername(currentUserName);
+ setBotType(currentBotName);
}
- // Get the actual bots aiml based response for this session
log.info("getResponse({})", text);
Response response = session.getResponse(text);
- // EEK! clean up the API!
- invoke("publishRequest", text); // publisher used by uis
+ invoke("publishRequest", text);
invoke("publishResponse", response);
invoke("publishText", response.msg);
return response;
}
- private Bot getBot(String botName) {
- return bots.get(botName).getBot();
+ private Bot getBot(String currentBotName) {
+ return bots.get(currentBotName).getBot();
}
- private BotInfo getBotInfo(String botName) {
- if (botName == null) {
+ private BotInfo getBotInfo(String currentBotName) {
+ if (currentBotName == null) {
error("getBotinfo(null) not valid");
return null;
}
- BotInfo botInfo = bots.get(botName);
+ BotInfo botInfo = bots.get(currentBotName);
if (botInfo == null) {
- error("botInfo(%s) is null", botName);
+ error("botInfo(%s) is null", currentBotName);
return null;
}
@@ -306,18 +315,30 @@ private BotInfo getBotInfo(String botName) {
* thing before forcing a different (default?) response instead.
*
* @param val
- * foo
+ * foo
*/
public void repetitionCount(int val) {
org.alicebot.ab.MagicNumbers.repetition_count = val;
}
+ /**
+ * get the "current" session if it exists
+ *
+ * @return
+ */
public Session getSession() {
- return getSession(getCurrentUserName(), getCurrentBotName());
+ return getSession(getUsername(), getBotType());
}
- public Session getSession(String userName, String botName) {
- String sessionKey = getSessionKey(userName, botName);
+ /**
+ * get a specific user & currentBotName session
+ *
+ * @param user
+ * @param currentBotName
+ * @return
+ */
+ public Session getSession(String user, String currentBotName) {
+ String sessionKey = getSessionKey(user, currentBotName);
if (sessions.containsKey(sessionKey)) {
return sessions.get(sessionKey);
} else {
@@ -325,25 +346,47 @@ public Session getSession(String userName, String botName) {
}
}
- public void removePredicate(String userName, String predicateName) {
- removePredicate(userName, getCurrentBotName(), predicateName);
+ /**
+ * remove a specific user and current bot types predicate
+ */
+ public void removePredicate(String user, String predicateName) {
+ removePredicate(user, getBotType(), predicateName);
}
- public void removePredicate(String userName, String botName, String predicateName) {
- getSession(userName, botName).remove(predicateName);
+ /**
+ * remove an explicit user and currentBotName's predicate
+ *
+ * @param user
+ * @param currentBotName
+ * @param name
+ */
+ public void removePredicate(String user, String currentBotName, String name) {
+ Session session = getSession(user, currentBotName);
+ if (session != null) {
+ session.remove(name);
+ } else {
+ error("could not remove predicate %s from session %s<->%s session does not exist", user, currentBotName, name);
+ }
}
/**
* Add a value to a set for the current session
*
* @param setName
- * name of the set
+ * name of the set
* @param setValue
- * value to add to the set
+ * value to add to the set
*/
public void addToSet(String setName, String setValue) {
+ if (setName == null || setValue == null) {
+ error("addToSet(%s,%s) cannot have name or value null", setName, setValue);
+ return;
+ }
+ setName = setName.toLowerCase().trim();
+ setValue = setValue.trim();
+
// add to the set for the bot.
- Bot bot = getBot(getCurrentBotName());
+ Bot bot = getBot(getBotType());
AIMLSet updateSet = bot.setMap.get(setName);
setValue = setValue.toUpperCase().trim();
if (updateSet != null) {
@@ -364,15 +407,23 @@ public void addToSet(String setName, String setValue) {
* Add a map / value for the current session
*
* @param mapName
- * - the map name
+ * - the map name
* @param key
- * - the key
+ * - the key
* @param value
- * - the value
+ * - the value
*/
public void addToMap(String mapName, String key, String value) {
+
+ if (mapName == null || key == null || value == null) {
+ error("addToMap(%s,%s,%s) mapname, key or value cannot be null", mapName, key, value);
+ return;
+ }
+ mapName = mapName.toLowerCase().trim();
+ key = key.toUpperCase().trim();
+
// add an entry to the map.
- Bot bot = getBot(getCurrentBotName());
+ Bot bot = getBot(getBotType());
AIMLMap updateMap = bot.mapMap.get(mapName);
key = key.toUpperCase().trim();
if (updateMap != null) {
@@ -388,41 +439,88 @@ public void addToMap(String mapName, String key, String value) {
}
}
- public void setPredicate(String predicateName, String predicateValue) {
- setPredicate(getCurrentUserName(), predicateName, predicateValue);
+ public void setPredicate(String name, String value) {
+ setPredicate(getUsername(), name, value);
}
- public void setPredicate(String userName, String predicateName, String predicateValue) {
- setPredicate(userName, getCurrentBotName(), predicateName, predicateValue);
+ /**
+ * Sets a specific user and current bot predicate to a value. Useful when
+ * setting predicate values of a session, when the user previously was an
+ * unknown human to a new or previously known user.
+ *
+ * @param currentUserName
+ * @param name
+ * @param value
+ */
+ public void setPredicate(String currentUserName, String name, String value) {
+ setPredicate(currentUserName, getBotType(), name, value);
}
- public void setPredicate(String userName, String botName, String predicateName, String predicateValue) {
- Session session = getSession(userName, botName);
+ /**
+ * Sets a predicate for a session keyed by currentUserName and bottype. If the
+ * session does not currently exist, it will make a new session for that user.
+ *
+ * @param currentUserName
+ * @param currentBotName
+ * @param name
+ * @param value
+ */
+ public void setPredicate(String currentUserName, String currentBotName, String name, String value) {
+ Session session = getSession(currentUserName, currentBotName);
if (session != null) {
- session.setPredicate(predicateName, predicateValue);
+ session.setPredicate(name, value);
+ } else {
+ // attempt to create a session if it doesn't exist
+ session = startSession(currentUserName, currentBotName, false);
+ if (session != null) {
+ session.setPredicate(name, value);
+ } else {
+ error("could not create session");
+ }
}
}
- @Deprecated
- public void unsetPredicate(String userName, String predicateName) {
- removePredicate(userName, getCurrentBotName(), predicateName);
+ @Deprecated /* use removePredicate */
+ public void unsetPredicate(String currentUserName, String predicateName) {
+ removePredicate(currentUserName, getBotType(), predicateName);
}
+ /**
+ * Get a predicate's value for the current session
+ *
+ * @param predicateName
+ * @return
+ */
public String getPredicate(String predicateName) {
- return getPredicate(getCurrentUserName(), predicateName);
+ return getPredicate(getUsername(), predicateName);
}
- public String getPredicate(String userName, String predicateName) {
- return getPredicate(userName, getCurrentBotName(), predicateName);
+ /**
+ * get a specified users's predicate value for the current currentBotName
+ * session
+ *
+ * @param currentUserName
+ * @param predicateName
+ * @return
+ */
+ public String getPredicate(String currentUserName, String predicateName) {
+ return getPredicate(currentUserName, getBotType(), predicateName);
}
- public String getPredicate(String userName, String botName, String predicateName) {
- Session s = getSession(userName, botName);
+ /**
+ * With a session key, get a specific predicate value
+ *
+ * @param currentUserName
+ * @param currentBotName
+ * @param predicateName
+ * @return
+ */
+ public String getPredicate(String currentUserName, String currentBotName, String predicateName) {
+ Session s = getSession(currentUserName, currentBotName);
if (s == null) {
- // If that session doesn't currently exist, let's start it.
- s = startSession(userName, botName);
+ s = startSession(currentUserName, currentBotName, false);
if (s == null) {
- log.warn("Error starting programAB session between bot {} and user {}", userName, botName);
+ log.warn("Error starting programAB session between bot {} and user {}", currentUserName, currentBotName);
return null;
}
}
@@ -432,21 +530,22 @@ public String getPredicate(String userName, String botName, String predicateName
/**
* Only respond if the last response was longer than delay ms ago
*
- * @param userName
- * - current userName
+ * @param currentUserName
+ * - current currentUserName
* @param text
- * - text to get a response
+ * - text to get a response
* @param delay
- * - min amount of time that must have transpired since the last
+ * - min amount of time that must have transpired since
+ * the last
* @return the response
* @throws IOException
- * boom
+ * boom
*/
- public Response troll(String userName, String text, Long delay) throws IOException {
- Session session = getSession(userName, getCurrentBotName());
+ public Response troll(String currentUserName, String text, Long delay) throws IOException {
+ Session session = getSession(currentUserName, getBotType());
long delta = System.currentTimeMillis() - session.lastResponseTime.getTime();
if (delta > delay) {
- return getResponse(userName, text);
+ return getResponse(currentUserName, text);
} else {
log.info("Skipping response, minimum delay since previous response not reached.");
return null;
@@ -461,13 +560,13 @@ public boolean isEnableAutoConversation() {
* Return a list of all patterns that the current AIML Bot knows to match
* against.
*
- * @param botName
- * the bots name from which to return it's patterns.
+ * @param currentBotName
+ * the bots name from which to return it's patterns.
* @return a list of all patterns loaded into the aiml brain
*/
- public ArrayList listPatterns(String botName) {
+ public ArrayList listPatterns(String currentBotName) {
ArrayList patterns = new ArrayList();
- Bot bot = getBot(botName);
+ Bot bot = getBot(currentBotName);
for (Category c : bot.brain.getCategories()) {
patterns.add(c.getPattern());
}
@@ -496,8 +595,8 @@ public void onText(String text) throws IOException {
/**
* @param response
- * publish a response generated from a session in the programAB
- * service.
+ * publish a response generated from a session in the programAB
+ * service.
* @return the response
*
*/
@@ -508,6 +607,9 @@ public Response publishResponse(Response response) {
@Override
public String publishText(String text) {
+ if (text == null || text.length() == 0) {
+ return "";
+ }
// TODO: this should not be done here.
// clean up whitespaces & cariage return
text = text.replaceAll("\\n", " ");
@@ -525,7 +627,7 @@ public String publishRequest(String text) {
* result of this is displayed in the chatbot debug console.
*
* @param oobText
- * the out of band text to publish
+ * the out of band text to publish
* @return oobtext
*
*/
@@ -535,21 +637,21 @@ public String publishOOBText(String oobText) {
/**
* This method will close the current bot, and reload it from AIML It then
- * will then re-establish only the session associated with userName.
+ * will then re-establish only the session associated with currentUserName.
*
- * @param userName
- * username for the session
- * @param botName
- * the bot name being chatted with
+ * @param currentUserName
+ * currentUserName for the session
+ * @param currentBotName
+ * the bot name being chatted with
* @throws IOException
- * boom
+ * boom
*
*/
- public void reloadSession(String userName, String botName) throws IOException {
- Session session = getSession(userName, botName);
+ public void reloadSession(String currentUserName, String currentBotName) throws IOException {
+ Session session = getSession(currentUserName, currentBotName);
if (session != null) {
session.reload();
- info("reloaded session %s <-> %s ", userName, botName);
+ info("reloaded session %s <-> %s ", currentUserName, currentBotName);
}
}
@@ -567,8 +669,8 @@ public Map getPredicates() {
*
* @return
*/
- public Map getPredicates(String userName, String botName) {
- Session session = getSession(userName, botName);
+ public Map getPredicates(String currentUserName, String currentBotName) {
+ Session session = getSession(currentUserName, currentBotName);
if (session != null) {
return session.getPredicates();
}
@@ -585,148 +687,136 @@ public void savePredicates() {
}
public void setEnableAutoConversation(boolean enableAutoConversation) {
- getSession().enableTrolling = enableAutoConversation;
+ getConfig().enableTrolling = enableAutoConversation;
}
- public void setMaxConversationDelay(int maxConversationDelay) {
- getSession().maxConversationDelay = maxConversationDelay;
+ public boolean getEnableAutoConversation() {
+ return getConfig().enableTrolling;
}
- public void setProcessOOB(boolean processOOB) {
- getSession().processOOB = processOOB;
+ public void setMaxConversationDelay(int maxConversationDelay) {
+ getConfig().maxConversationDelay = maxConversationDelay;
}
/**
* set a bot property - the result will be serialized to config/properties.txt
*
* @param name
- * property name to set for current bot/session
+ * property name to set for current bot/session
* @param value
- * value to set for current bot/session
+ * value to set for current bot/session
*/
public void setBotProperty(String name, String value) {
- setBotProperty(getCurrentBotName(), name, value);
+ setBotProperty(getBotType(), name, value);
}
/**
* set a bot property - the result will be serialized to config/properties.txt
*
- * @param botName
- * bot name
+ * @param currentBotName
+ * bot name
* @param name
- * bot property name
+ * bot property name
* @param value
- * value to set the property too
+ * value to set the property too
*/
- public void setBotProperty(String botName, String name, String value) {
- info("setting %s property %s:%s", getCurrentBotName(), name, value);
- BotInfo botInfo = getBotInfo(botName);
+ public void setBotProperty(String currentBotName, String name, String value) {
+ info("setting %s property %s:%s", getBotType(), name, value);
+ BotInfo botInfo = getBotInfo(currentBotName);
name = name.trim();
value = value.trim();
botInfo.setProperty(name, value);
}
public void removeBotProperty(String name) {
- removeBotProperty(getCurrentBotName(), name);
+ removeBotProperty(getBotType(), name);
}
- public void removeBotProperty(String botName, String name) {
- info("removing %s property %s", getCurrentBotName(), name);
- BotInfo botInfo = getBotInfo(botName);
+ public void removeBotProperty(String currentBotName, String name) {
+ info("removing %s property %s", getBotType(), name);
+ BotInfo botInfo = getBotInfo(currentBotName);
botInfo.removeProperty(name);
}
- public Session startSession() throws IOException {
- return startSession(config.currentUserName);
- }
-
- // FIXME - it should just set the current userName only
- public Session startSession(String userName) throws IOException {
- return startSession(userName, getCurrentBotName());
+ /**
+ * Setting a session is only setting a key, to the active user and bot, its
+ * not starting a session, which is a different process done threw
+ * startSession.
+ *
+ * Sets currentUserName and currentBotName. The session will be started if it
+ * can be
+ * when a
+ * getResponse is processed. "Active" session is just where the session key
+ * exists and is currently set via currentUserName and currentBotName
+ *
+ * @param currentUserName
+ * @param currentBotName
+ * @return
+ */
+ public void setSession(String currentUserName, String currentBotName) {
+ // replacing "focus" so
+ // current name and bottype is the
+ // one that will be used
+ setUsername(currentUserName);
+ setBotType(currentBotName);
}
-
- public Session startSession(String userName, String botName) {
- return startSession(null, userName, botName, MagicBooleans.defaultLocale);
+
+ public Session startSession(String currentUserName) {
+ return startSession(currentUserName, getBotType(), true);
}
-
- @Deprecated /* path included for legacy */
- public Session startSession(String path, String userName, String botName) {
- return startSession(path, userName, botName, MagicBooleans.defaultLocale);
+
+ public Session startSession(String currentUserName, String currentBotName) {
+ return startSession(currentUserName, currentBotName, true);
}
/**
* Load the AIML 2.0 Bot config and start a chat session. This must be called
- * after the service is created.
- *
- * @param path
- * - the path to the ProgramAB directory where the bots aiml and
- * config reside
- * @param userName
- * - The new user name
- * @param botName
- * - The name of the bot to load. (example: alice2)
- * @param locale
- * - The locale of the bot to ensure the aiml is loaded (mostly for
- * Japanese support) FIXME - local is defined in the bot,
- * specifically config/mrl.properties
+ * after the service is created. If the session does not exist it will be
+ * created. If the session does exist then that session will be used.
*
- * reasons to deprecate:
+ * config.currentUserName and config.currentBotName will be set in memory the
+ * specified
+ * values. The "current" session will be this session.
*
- * 1. I question the need to expose this externally at all - if the
- * user uses getResponse(username, botname, text) then a session can
- * be auto-started - there is really no reason not to auto-start.
+ * @param currentUserName
+ * - The new user name
+ * @param currentBotName
+ * - The name of the bot to load. (example: alice2)
*
- * 2. path is completely invalid here
- *
- * 3. Locale is completely invalid - it is now part of the bot
- * description in mrl.properties and shouldn't be defined externally,
- * unles its pulled from Runtime
* @return the session that is started
*/
- public Session startSession(String path, String userName, String botName, java.util.Locale locale) {
+ public Session startSession(String currentUserName, String currentBotName, boolean setAsCurrent) {
- /*
- * not wanted or needed if (path != null) { addBotPath(path); }
- */
-
- Session session = getSession(userName, botName);
-
- if (session != null) {
- log.info("session {} already exists - will use it", getSessionKey(userName, botName));
- setCurrentSession(userName, botName);
- return session;
+ if (currentUserName == null || currentBotName == null) {
+ error("currentUserName nor bot type can be null");
+ return null;
}
- // create a new session
- log.info("creating new sessions");
- BotInfo botInfo = getBotInfo(botName);
- if (botInfo == null) {
- error("cannot create session %s is not a valid botName", botName);
+ if (!bots.containsKey(currentBotName)) {
+ error("bot type %s is not valid, list of possible types are %s", currentBotName, bots.keySet());
return null;
}
- session = new Session(this, userName, botInfo);
- sessions.put(getSessionKey(userName, botName), session);
+ if (setAsCurrent) {
+ // really sets the key of the active session currentUserName <-> currentBotName
+ // but next getResponse will use this session
+ setSession(currentUserName, currentBotName);
+ }
- log.info("Started session for bot botName:{} , userName:{}", botName, userName);
- setCurrentSession(userName, botName);
- return session;
- }
+ String sessionKey = getSessionKey(currentUserName, currentBotName);
+ if (sessions.containsKey(sessionKey)) {
+ log.info("session exists returning existing");
+ return sessions.get(sessionKey);
+ }
- /**
- * setting the current session is equivalent to setting current user name and
- * current bot name
- *
- * @param userName
- * username
- * @param botName
- * botname
- *
- */
- public void setCurrentSession(String userName, String botName) {
- setCurrentUserName(userName);
- setCurrentBotName(botName);
+ log.info("creating new session {}<->{} replacing {}", currentUserName, currentBotName, setAsCurrent);
+ BotInfo botInfo = getBotInfo(currentBotName);
+ Session session = new Session(this, currentUserName, botInfo);
+ sessions.put(sessionKey, session);
+
+ // get session
+ return getSession();
}
/**
@@ -736,7 +826,7 @@ public void setCurrentSession(String userName, String botName) {
* @param c
*/
public void addCategory(Category c) {
- Bot bot = getBot(getCurrentBotName());
+ Bot bot = getBot(getBotType());
bot.brain.addCategory(c);
}
@@ -758,16 +848,18 @@ public void addCategory(String pattern, String template, String that) {
public void addCategory(String pattern, String template) {
addCategory(pattern, template, "*");
}
-
+
/**
- * Verifies and adds a new path to the search directories for bots
+ * Verifies and adds a new path to the search directories for bots. Bots of
+ * aiml live in directories which represent their "type" The directory names
+ * must be unique.
*
* @param path
- * the path to add a bot from
+ * the path to add a bot from
* @return the path if successful. o/w null
*
*/
- public String addBotPath(String path) {
+ public String addBot(String path) {
// verify the path is valid
File botPath = new File(path);
File verifyAiml = new File(FileIO.gluePaths(path, "aiml"));
@@ -783,12 +875,12 @@ public String addBotPath(String path) {
BotInfo botInfo = new BotInfo(this, botPath);
- // key'ing on "path" probably would be better and only displaying "name"
- // then there would be no put/collisions only duplicate names
- // (preferrable)
+ if (bots.containsKey(botInfo.currentBotName)) {
+ log.info("replacing bot %s with new bot definition", botInfo.currentBotName);
+ }
- bots.put(botInfo.name, botInfo);
- botInfo.img = getBotImage(botInfo.name);
+ bots.put(botInfo.currentBotName, botInfo);
+ botInfo.img = getBotImage(botInfo.currentBotName);
broadcastState();
} else {
@@ -798,40 +890,78 @@ public String addBotPath(String path) {
return path;
}
- @Deprecated /* for legacy - use addBotsDir */
- public String setPath(String path) {
- // This method is not good, because it doesn't take the full path
- // from input and there is a buried "hardcoded" value which no one knows
- // about
- addBotsDir(path + File.separator + "bots");
-
- return path;
+ @Deprecated /* use setBotType */
+ public void setCurrentBotName(String currentBotName) {
+ setBotType(currentBotName);
}
- public void setCurrentBotName(String botName) {
- config.currentBotName = botName;
- invoke("getBotImage", botName);
- broadcastState();
+ /**
+ * Sets the current bot type to a set of aiml folders previously added via
+ * configuration or through the addBot(path) function.
+ *
+ * You can get a list of possible configured bot types through the method
+ * getBots()
+ *
+ * @param currentBotName
+ */
+ public void setBotType(String currentBotName) {
+ if (currentBotName == null) {
+ error("bot type cannot be null");
+ return;
+ }
+
+ if (bots.size() == 0) {
+ error("bot paths must be set before a bot type is set");
+ }
+
+ if (!bots.containsKey(currentBotName)) {
+ error("cannot set bot %s, no valid type found, possible values are %s", currentBotName, bots.keySet());
+ return;
+ }
+ String prev = config.currentBotName;
+ config.currentBotName = currentBotName;
+ if (!currentBotName.equals(prev)) {
+ invoke("getBotImage", currentBotName);
+ broadcastState();
+ }
}
- public void setCurrentUserName(String currentUserName) {
+ public void setUsername(String currentUserName) {
+ if (currentUserName == null) {
+ error("currentUserName cannot be null");
+ return;
+ }
+ String prev = config.currentUserName;
config.currentUserName = currentUserName;
- broadcastState();
+ if (!currentUserName.equals(prev)) {
+ broadcastState();
+ }
}
- public Session getCurrentSession() {
- return sessions.get(getSessionKey(getCurrentUserName(), getCurrentBotName()));
+ public String getSessionKey(String currentUserName, String currentBotName) {
+ return String.format("%s <-> %s", currentUserName, currentBotName);
}
- public String getSessionKey(String userName, String botName) {
- return String.format("%s <-> %s", userName, botName);
+ /**
+ * Simple preferred way to get the user's name
+ *
+ * @return
+ */
+ public String getUsername() {
+ return config.currentUserName;
}
+ @Deprecated /* of course it will be "current" - use getUser() */
public String getCurrentUserName() {
- return config.currentUserName;
+ return getUsername();
}
+ @Deprecated /* use getBotType() */
public String getCurrentBotName() {
+ return getBotType();
+ }
+
+ public String getBotType() {
return config.currentBotName;
}
@@ -930,19 +1060,52 @@ public boolean setPeerSearch(boolean b) {
@Override
public void startService() {
- super.startService();
+ try {
+ super.startService();
- logPublisher = new SimpleLogPublisher(this);
- logPublisher.filterClasses(new String[] { "org.alicebot.ab.Graphmaster", "org.alicebot.ab.MagicBooleans", "class org.myrobotlab.programab.MrlSraixHandler" });
- Logging logging = LoggingFactory.getInstance();
- logging.setLevel("org.alicebot.ab.Graphmaster", "DEBUG");
- logging.setLevel("org.alicebot.ab.MagicBooleans", "DEBUG");
- logging.setLevel("class org.myrobotlab.programab.MrlSraixHandler", "DEBUG");
- logPublisher.start();
+ logPublisher = new SimpleLogPublisher(this);
+ logPublisher.filterClasses(new String[] { "org.alicebot.ab.Graphmaster", "org.alicebot.ab.MagicBooleans",
+ "class org.myrobotlab.programab.MrlSraixHandler" });
+ Logging logging = LoggingFactory.getInstance();
+ logging.setLevel("org.alicebot.ab.Graphmaster", "DEBUG");
+ logging.setLevel("org.alicebot.ab.MagicBooleans", "DEBUG");
+ logging.setLevel("class org.myrobotlab.programab.MrlSraixHandler", "DEBUG");
+ logPublisher.start();
- scanForBots(getResourceDir());
+ } catch (Exception e) {
+ error(e);
+ }
}
+
+ @Override
+ public ProgramABConfig apply(ProgramABConfig c) {
+ super.apply(c);
+ // scan for bots
+ if (config.botDir != null) {
+ scanForBots(config.botDir);
+ }
+
+ // explicitly setting bots overrides scans
+ if (config.bots != null && config.bots.size() > 0) {
+ for (String botPath : config.bots) {
+ addBot(botPath);
+ }
+ }
+
+ if (config.currentUserName != null) {
+ setUsername(config.currentUserName);
+ }
+
+ if (config.currentBotName != null) {
+ setBotType(config.currentBotName);
+ }
+
+ if (config.startTopic != null) {
+ setTopic(config.startTopic);
+ }
+ return c;
+ }
@Override /* FIXME - just do this once in abstract */
public void attachTextListener(TextListener service) {
@@ -999,24 +1162,28 @@ public BotInfo getBotInfo() {
* reload current session
*
* @throws IOException
- * boom
+ * boom
*
*/
- public void reload() throws IOException {
- reloadSession(getCurrentUserName(), getCurrentBotName());
+ public void reload() {
+ try {
+ reloadSession(getUsername(), getBotType());
+ } catch (Exception e) {
+ error(e);
+ }
}
public String getBotImage() {
- return getBotImage(getCurrentBotName());
+ return getBotImage(getBotType());
}
- public String getBotImage(String botName) {
+ public String getBotImage(String currentBotName) {
BotInfo botInfo = null;
String path = null;
try {
- botInfo = getBotInfo(botName);
+ botInfo = getBotInfo(currentBotName);
if (botInfo != null) {
path = FileIO.gluePaths(botInfo.path.getAbsolutePath(), "bot.png");
File check = new File(path);
@@ -1026,16 +1193,16 @@ public String getBotImage(String botName) {
}
} catch (Exception e) {
- info("image for %s cannot be found %s", botName, e.getMessage());
+ info("image for %s cannot be found %s", currentBotName, e.getMessage());
}
return getResourceImage("default.png");
}
- public String getAimlFile(String botName, String name) {
- BotInfo botInfo = getBotInfo(botName);
+ public String getAimlFile(String currentBotName, String name) {
+ BotInfo botInfo = getBotInfo(currentBotName);
if (botInfo == null) {
- error("cannot get bot %s", botName);
+ error("cannot get bot %s", currentBotName);
return null;
}
@@ -1053,10 +1220,10 @@ public String getAimlFile(String botName, String name) {
return ret;
}
- public void saveAimlFile(String botName, String filename, String data) {
- BotInfo botInfo = getBotInfo(botName);
+ public void saveAimlFile(String currentBotName, String filename, String data) {
+ BotInfo botInfo = getBotInfo(currentBotName);
if (botInfo == null) {
- error("cannot get bot %s", botName);
+ error("cannot get bot %s", currentBotName);
return;
}
@@ -1090,44 +1257,10 @@ public ProgramABConfig getConfig() {
return config;
}
- @Override
- public ProgramABConfig apply(ProgramABConfig c) {
- super.apply(c);
- if (c.bots != null && c.bots.size() > 0) {
- // bots.clear();
- for (String botPath : c.bots) {
- addBotPath(botPath);
- }
- }
-
- if (c.botDir == null) {
- c.botDir = getResourceDir();
- }
-
- List botsFromScanning = scanForBots(c.botDir);
- for (File file : botsFromScanning) {
- addBotPath(file.getAbsolutePath());
- }
-
- if (c.currentUserName != null) {
- setCurrentUserName(c.currentUserName);
- }
-
- if (c.currentBotName != null) {
- setCurrentBotName(c.currentBotName);
- }
-
- if (c.startTopic != null) {
- setTopic(c.startTopic);
- }
-
-
- return c;
- }
-
public static void main(String args[]) {
try {
LoggingFactory.init("INFO");
+ Runtime.startConfig("dev");
// Runtime.start("gui", "SwingGui");
Runtime runtime = Runtime.getInstance();
@@ -1164,7 +1297,7 @@ public static void main(String args[]) {
}
}
- public void addBotsDir(String path) {
+ public void addBots(String path) {
if (path == null) {
error("set path can not be null");
@@ -1182,25 +1315,26 @@ public void addBotsDir(String path) {
if (check.exists() && check.isDirectory()) {
log.info("found %d possible bot directories", check.listFiles().length);
for (File f : check.listFiles()) {
- addBotPath(f.getAbsolutePath());
+ addBot(f.getAbsolutePath());
}
}
}
@Override
- synchronized public void onChangePredicate(Chat chat, String predicateName, String result) {
- log.info("{} on predicate change {}={}", chat.bot.name, predicateName, result);
+ synchronized public void onChangePredicate(Chat chat, String predicateName, String value) {
+ log.info("{} on predicate change {}={}", chat.bot.name, predicateName, value);
// a little janky because program-ab doesn't know the predicate filename,
// because it does know the "user"
- // but ProgramAB saves predicates in a {username}.predicates.txt format in
+ // but ProgramAB saves predicates in a {currentUserName}.predicates.txt format
+ // in
// the bot directory
// so we find the session by matching the chat in the callback
for (Session s : sessions.values()) {
if (s.chat == chat) {
// found session saving predicates
- invoke("publishPredicate", s, predicateName, result);
+ invoke("publishPredicate", s, predicateName, value);
s.savePredicates();
return;
}
@@ -1214,24 +1348,26 @@ synchronized public void onChangePredicate(Chat chat, String predicateName, Stri
* extract current user and bot this is relevant to.
*
* @param session
- * - session where the predicate change occurred
+ * - session where the predicate change occurred
* @param name
- * - name of predicate
+ * - name of predicate
* @param value
- * - new value of predicate
+ * - new value of predicate
* @return
*/
- public PredicateEvent publishPredicate(Session session, String name, String value) {
- PredicateEvent event = new PredicateEvent();
- event.id = String.format("%s<->%s", session.userName, session.botInfo.name);
- event.userName = session.userName;
- event.botName = session.botInfo.name;
+ public Event publishPredicate(Session session, String name, String value) {
+ Event event = new Event();
+ event.id = String.format("%s<->%s", session.userName, session.botInfo.currentBotName);
+ event.user = session.userName;
+ event.botname = session.botInfo.currentBotName;
event.name = name;
event.value = value;
if ("topic".equals(name) && value != null && !value.equals(session.currentTopic)) {
- invoke("publishTopic", new TopicChange(session.userName, session.botInfo.name, value, session.currentTopic));
+ Event topicChange = new Event(getName(), session.userName, session.botInfo.currentBotName, value);
+ invoke("publishTopic", topicChange);
session.currentTopic = value;
+ topicHistory.add(topicChange);
}
return event;
@@ -1263,7 +1399,8 @@ synchronized public void addCategoryToFile(Bot bot, Category c) {
if (!learnfFile.exists()) {
StringBuilder sb = new StringBuilder("\n");
- sb.append("\n");
+ sb.append(
+ "\n");
sb.append("\n");
sb.append("\n");
FileIO.toFile(learnfFile, sb.toString().getBytes());
@@ -1302,14 +1439,14 @@ public void sleep() {
@Override
public void onUtterance(Utterance utterance) throws Exception {
-
- log.info("Utterance Received " + utterance);
+
+ log.info("utterance received {}", utterance);
boolean talkToBots = false;
- // TODO: reconcile having different name between the discord bot username
+ // TODO: reconcile having different name between the discord bot currentUserName
// and the programab bot name. Mr. Turing is not actually Alice.. and vice
// versa.
- String botName = utterance.channelBotName;
+ String currentBotName = utterance.channelBotName;
// prevent bots going off the rails
if (utterance.isBot && talkToBots) {
@@ -1318,7 +1455,7 @@ public void onUtterance(Utterance utterance) throws Exception {
}
// Don't talk to myself, though I should be a bot..
- if (utterance.username.contentEquals(botName)) {
+ if (utterance.username != null && utterance.username.contentEquals(currentBotName)) {
log.info("Don't talk to myself.");
return;
}
@@ -1332,7 +1469,7 @@ public void onUtterance(Utterance utterance) throws Exception {
// TODO: don't talk to bots.. it won't go well..
// TODO: the discord api can provide use the list of mentioned users.
// for now.. we'll just see if we see Mr. Turing as a substring.
- config.sleep = (config.sleep || utterance.text.contains("@")) && !utterance.text.contains(botName);
+ config.sleep = (config.sleep || utterance.text.contains("@")) && !utterance.text.contains(currentBotName);
if (!config.sleep) {
shouldIRespond = true;
}
@@ -1346,7 +1483,7 @@ public void onUtterance(Utterance utterance) throws Exception {
String utteranceDisp = utterance.text;
// let's strip the @+botname from the beginning of the utterance i guess.
// Strip the botname from the utterance passed to programab.
- utteranceDisp = utteranceDisp.replace("@" + botName, "");
+ utteranceDisp = utteranceDisp.replace("@" + currentBotName, "");
Response resp = getResponse(utterance.username, utteranceDisp);
if (resp != null && !StringUtils.isEmpty(resp.msg)) {
// Ok.. now what? respond to the user ...
@@ -1367,17 +1504,18 @@ public void onUtterance(Utterance utterance) throws Exception {
}
}
}
-
+
/**
* This receiver can take a config published by another service and sync
* predicates from it
+ *
* @param cfg
*/
public void onConfig(ServiceConfig cfg) {
- Yaml yaml = new Yaml();
+ Yaml yaml = new Yaml();
String yml = yaml.dumpAsMap(cfg);
Map cfgMap = yaml.load(yml);
-
+
for (Map.Entry entry : cfgMap.entrySet()) {
if (entry.getValue() == null) {
setPredicate("cfg_" + entry.getKey(), null);
@@ -1385,7 +1523,7 @@ public void onConfig(ServiceConfig cfg) {
setPredicate("cfg_" + entry.getKey(), entry.getValue().toString());
}
}
-
+
invoke("getPredicates");
}
@@ -1394,24 +1532,80 @@ public Utterance publishUtterance(Utterance utterance) {
return utterance;
}
- public TopicChange publishTopic(TopicChange topicChange) {
+ /**
+ * New topic published when it changes
+ *
+ * @param topicChange
+ * @return
+ */
+ public Event publishTopic(Event topicChange) {
return topicChange;
}
- public String getTopic() {
- return getPredicate(getCurrentUserName(), "topic");
+ public String getTopic() {
+ return getPredicate(getUsername(), "topic");
}
-
- public String getTopic(String username) {
- return getPredicate(username, "topic");
+
+ public String getTopic(String currentUserName) {
+ return getPredicate(currentUserName, "topic");
}
-
- public void setTopic(String username, String topic) {
- setPredicate(username, "topic", topic);
+
+ public void setTopic(String currentUserName, String topic) {
+ setPredicate(currentUserName, "topic", topic);
}
-
- public void setTopic(String topic) {
- setPredicate(getCurrentUserName(), "topic", topic);
+
+ public void setTopic(String topic) {
+ setPredicate(getUsername(), "topic", topic);
+ }
+
+ /**
+ * Published when a new session is created
+ *
+ * @param session
+ * @return
+ */
+ public Event publishSession(Event session) {
+ return session;
+ }
+
+ /**
+ * clear all sessions
+ */
+ public void clear() {
+ log.info("clearing sessions");
+ sessions.clear();
+ }
+
+ /**
+ *
+ * A mechanism to publish a message directly from aiml.
+ * The subscriber can interpret the message and do something with it.
+ * In the case of InMoov for example, the unaddressed messages are processed
+ * as python method calls. This remove direct addressing from the aiml!
+ * And allows a great amount of flexibility on how the messages are
+ * interpreted, without polluting the aiml or ProgramAB.
+ *
+ * The oob syntax is:
+ * <oob>
+ * <mrljson>
+ * [{method:on_new_user, data:[{"name":"<star/>"}]}]
+ * </mrljson>
+ * </oob>
+ *
+ *
+ * Full typed parameters are supported without conversions.
+ *
+ *
+ *
+ * @param msg
+ * @return
+ */
+ public Message publishMessage(Message msg) {
+ return msg;
+ }
+
+ public OobProcessor getOobProcessor() {
+ return oobProcessor;
}
}
diff --git a/src/main/java/org/myrobotlab/service/Runtime.java b/src/main/java/org/myrobotlab/service/Runtime.java
index 61cf601a10..a9fc347782 100644
--- a/src/main/java/org/myrobotlab/service/Runtime.java
+++ b/src/main/java/org/myrobotlab/service/Runtime.java
@@ -127,8 +127,9 @@
* VAR OF RUNTIME !
*
*/
-public class Runtime extends Service implements MessageListener, ServiceLifeCyclePublisher, RemoteMessageHandler, ConnectionManager, Gateway, LocaleProvider {
-
+public class Runtime extends Service implements MessageListener, ServiceLifeCyclePublisher,
+ RemoteMessageHandler, ConnectionManager, Gateway, LocaleProvider {
+
final static private long serialVersionUID = 1L;
// FIXME - AVOID STATIC FIELDS !!! use .getInstance() to get the singleton
@@ -171,14 +172,22 @@ public class Runtime extends Service implements MessageListener,
* interested in filtering out if we want to maintain a data structure which
* has "interfaces of interest"
*/
- protected final static Set FILTERED_INTERFACES = new HashSet<>(Arrays.asList("org.myrobotlab.framework.interfaces.Broadcaster",
- "org.myrobotlab.service.interfaces.QueueReporter", "org.myrobotlab.framework.interfaces.ServiceQueue", "org.myrobotlab.framework.interfaces.MessageSubscriber",
- "org.myrobotlab.framework.interfaces.Invoker", "java.lang.Runnable", "org.myrobotlab.framework.interfaces.ServiceStatus", "org.atmosphere.nettosphere.Handler",
- "org.myrobotlab.framework.interfaces.NameProvider", "org.myrobotlab.framework.interfaces.NameTypeProvider", "org.myrobotlab.framework.interfaces.ServiceInterface",
- "org.myrobotlab.framework.interfaces.TaskManager", "org.myrobotlab.framework.interfaces.LoggingSink", "org.myrobotlab.framework.interfaces.StatusPublisher",
- "org.myrobotlab.framework.interfaces.TypeProvider", "java.io.Serializable", "org.myrobotlab.framework.interfaces.Attachable",
- "org.myrobotlab.framework.interfaces.StateSaver", "org.myrobotlab.framework.interfaces.MessageSender", "java.lang.Comparable",
- "org.myrobotlab.service.interfaces.ServiceLifeCycleListener", "org.myrobotlab.framework.interfaces.StatePublisher"));
+ protected final static Set FILTERED_INTERFACES = new HashSet<>(
+ Arrays.asList("org.myrobotlab.framework.interfaces.Broadcaster",
+ "org.myrobotlab.service.interfaces.QueueReporter", "org.myrobotlab.framework.interfaces.ServiceQueue",
+ "org.myrobotlab.framework.interfaces.MessageSubscriber",
+ "org.myrobotlab.framework.interfaces.Invoker", "java.lang.Runnable",
+ "org.myrobotlab.framework.interfaces.ServiceStatus", "org.atmosphere.nettosphere.Handler",
+ "org.myrobotlab.framework.interfaces.NameProvider", "org.myrobotlab.framework.interfaces.NameTypeProvider",
+ "org.myrobotlab.framework.interfaces.ServiceInterface",
+ "org.myrobotlab.framework.interfaces.TaskManager", "org.myrobotlab.framework.interfaces.LoggingSink",
+ "org.myrobotlab.framework.interfaces.StatusPublisher",
+ "org.myrobotlab.framework.interfaces.TypeProvider", "java.io.Serializable",
+ "org.myrobotlab.framework.interfaces.Attachable",
+ "org.myrobotlab.framework.interfaces.StateSaver", "org.myrobotlab.framework.interfaces.MessageSender",
+ "java.lang.Comparable",
+ "org.myrobotlab.service.interfaces.ServiceLifeCycleListener",
+ "org.myrobotlab.framework.interfaces.StatePublisher"));
protected final Set serviceTypes = new HashSet<>();
@@ -372,9 +381,9 @@ static public ServiceInterface create(String name) {
*
*
* @param name
- * - Required, cannot be null
+ * - Required, cannot be null
* @param type
- * - Can be null if a service file exists for named service
+ * - Can be null if a service file exists for named service
* @return the service
*/
static public synchronized ServiceInterface create(String name, String type) {
@@ -413,7 +422,8 @@ static public synchronized ServiceInterface create(String name, String type) {
* @param name
* @return
*/
- synchronized private static Map createServicesFromPlan(Plan plan, Map createdServices, String name) {
+ synchronized private static Map createServicesFromPlan(Plan plan,
+ Map createdServices, String name) {
if (createdServices == null) {
createdServices = new LinkedHashMap<>();
@@ -442,7 +452,8 @@ synchronized private static Map createServicesFromPlan
try {
((ConfigurableService) si).apply(sc);
} catch (Exception e) {
- Runtime.getInstance().error("could not apply config of type %s to service %s, using default config", sc.type, si.getName(), sc.type);
+ Runtime.getInstance().error("could not apply config of type %s to service %s, using default config", sc.type,
+ si.getName(), sc.type);
}
}
createdServices.put(service, si);
@@ -453,7 +464,8 @@ synchronized private static Map createServicesFromPlan
}
public String getServiceExample(String serviceType) {
- String url = "https://raw.githubusercontent.com/MyRobotLab/myrobotlab/develop/src/main/resources/resource/" + serviceType + "/" + serviceType + ".py";
+ String url = "https://raw.githubusercontent.com/MyRobotLab/myrobotlab/develop/src/main/resources/resource/"
+ + serviceType + "/" + serviceType + ".py";
byte[] bytes = Http.get(url);
if (bytes != null) {
return new String(bytes);
@@ -461,7 +473,8 @@ public String getServiceExample(String serviceType) {
return "";
}
- public static String getPeerName(String peerKey, ServiceConfig config, Map peers, String parentName) {
+ public static String getPeerName(String peerKey, ServiceConfig config, Map peers,
+ String parentName) {
if (peerKey == null || !peers.containsKey(peerKey)) {
return null;
@@ -522,9 +535,9 @@ public static void check(String name, String type) {
* Use {@link #start(String, String)} instead.
*
* @param name
- * Name of service
+ * Name of service
* @param type
- * Type of service
+ * Type of service
* @return Created service
*/
@Deprecated /* use start */
@@ -536,7 +549,7 @@ static public ServiceInterface createAndStart(String name, String type) {
* creates and starts services from a cmd line object
*
* @param services
- * - services to be created
+ * - services to be created
*/
public final static void createAndStartServices(List services) {
@@ -617,7 +630,7 @@ static public boolean setAllVirtual(boolean b) {
*
* @param autoStart
* @throws IOException
- * - thrown if cannot write file to filesystem
+ * - thrown if cannot write file to filesystem
*/
public void setAutoStart(boolean autoStart) throws IOException {
log.debug("setAutoStart {}", autoStart);
@@ -646,11 +659,11 @@ public void setAutoStart(boolean autoStart) throws IOException {
* the service was originally instantiated under.
*
* @param name
- * May not contain '/' or '@', i.e. cannot be a full name
+ * May not contain '/' or '@', i.e. cannot be a full name
* @param type
- * The type of the new service
+ * The type of the new service
* @param inId
- * The ID of the runtime the service is linked to.
+ * The ID of the runtime the service is linked to.
* @return An existing service if the requested name and type match, otherwise
* a newly created service. If the name is null, or it contains '@' or
* '/', or a service with the same name exists but has a different
@@ -704,7 +717,8 @@ static private synchronized ServiceInterface createService(String name, String t
ServiceInterface si = Runtime.getService(fullName);
if (si != null) {
if (!si.getTypeKey().equals(fullTypeName)) {
- runtime.error("Service with name {} already exists but is of type {} while requested type is ", name, si.getTypeKey(), type);
+ runtime.error("Service with name {} already exists but is of type {} while requested type is ", name,
+ si.getTypeKey(), type);
return null;
}
return si;
@@ -737,9 +751,11 @@ static private synchronized ServiceInterface createService(String name, String t
log.debug("loader for this class " + Runtime.class.getClassLoader().getClass().getCanonicalName());
log.debug("parent " + Runtime.class.getClassLoader().getParent().getClass().getCanonicalName());
log.debug("system class loader " + ClassLoader.getSystemClassLoader());
- log.debug("parent should be null" + ClassLoader.getSystemClassLoader().getParent().getClass().getCanonicalName());
+ log.debug(
+ "parent should be null" + ClassLoader.getSystemClassLoader().getParent().getClass().getCanonicalName());
log.debug("thread context " + Thread.currentThread().getContextClassLoader().getClass().getCanonicalName());
- log.debug("thread context parent " + Thread.currentThread().getContextClassLoader().getParent().getClass().getCanonicalName());
+ log.debug("thread context parent "
+ + Thread.currentThread().getContextClassLoader().getParent().getClass().getCanonicalName());
}
// FIXME - error if deps are missing - prompt license
@@ -841,7 +857,7 @@ public static final void gc() {
*
* @return external or routers ip
* @throws Exception
- * e
+ * e
*/
public static String getExternalIp() throws Exception {
URL whatismyip = new URL("http://checkip.amazonaws.com");
@@ -1098,7 +1114,8 @@ public static Map getLocalServices() {
Map local = new HashMap<>();
for (String serviceName : registry.keySet()) {
// FIXME @ should be a requirement of "all" entries for consistency
- if (!serviceName.contains("@") || serviceName.endsWith(String.format("@%s", Platform.getLocalInstance().getId()))) {
+ if (!serviceName.contains("@")
+ || serviceName.endsWith(String.format("@%s", Platform.getLocalInstance().getId()))) {
local.put(serviceName, registry.get(serviceName));
}
}
@@ -1149,7 +1166,8 @@ public static Map getMethodMap(String inName) {
* @return list of registrations
*/
synchronized public List getServiceList() {
- return registry.values().stream().map(si -> new Registration(si.getId(), si.getName(), si.getTypeKey())).collect(Collectors.toList());
+ return registry.values().stream().map(si -> new Registration(si.getId(), si.getName(), si.getTypeKey()))
+ .collect(Collectors.toList());
}
// FIXME - scary function - returns private data
@@ -1162,7 +1180,8 @@ public static ServiceInterface getService(String inName) {
});
}
- public static > S getConfigurableService(String inName, StaticType serviceType) {
+ public static > S getConfigurableService(
+ String inName, StaticType serviceType) {
return getService(inName, serviceType);
}
@@ -1171,7 +1190,7 @@ public static getServiceNames(String pattern) {
- return getServices().stream().map(NameProvider::getName).filter(serviceName -> match(serviceName, pattern)).collect(Collectors.toList());
+ return getServices().stream().map(NameProvider::getName).filter(serviceName -> match(serviceName, pattern))
+ .collect(Collectors.toList());
}
/**
* @param interfaze
- * the interface
+ * the interface
* @return a list of service names that implement the interface
* @throws ClassNotFoundException
- * if the class for the requested interface is not found.
+ * if the class for the requested interface is
+ * not found.
*
*/
public static List getServiceNamesFromInterface(String interfaze) throws ClassNotFoundException {
@@ -1241,7 +1262,7 @@ public static List getServiceNamesFromInterface(String interfaze) throws
/**
* @param interfaze
- * interface
+ * interface
* @return list of service names
*
*/
@@ -1262,7 +1283,7 @@ public static List getServices() {
* Get all services that belong to an MRL instance with the given ID.
*
* @param id
- * The ID of the MRL instance
+ * The ID of the MRL instance
* @return A list of the services that belong to the given MRL instance
*/
public static List getServices(String id) {
@@ -1284,7 +1305,7 @@ public static List getServices(String id) {
/**
* @param interfaze
- * interface
+ * interface
* @return results
*
*/
@@ -1305,7 +1326,8 @@ public ServiceTypeNameResults getServiceTypeNamesFromInterface(String interfaze)
Set> ancestry = new HashSet<>();
Class> targetClass = Class.forName(st.getType()); // this.getClass();
- while (targetClass.getCanonicalName().startsWith("org.myrobotlab") && !targetClass.getCanonicalName().startsWith("org.myrobotlab.framework")) {
+ while (targetClass.getCanonicalName().startsWith("org.myrobotlab")
+ && !targetClass.getCanonicalName().startsWith("org.myrobotlab.framework")) {
ancestry.add(targetClass);
targetClass = targetClass.getSuperclass();
}
@@ -1334,7 +1356,7 @@ public ServiceTypeNameResults getServiceTypeNamesFromInterface(String interfaze)
* specific interface
*
* @param interfaze
- * class
+ * class
* @return list of service interfaces
*
*/
@@ -1442,7 +1464,7 @@ public static String getPlatformInfo() {
* Get a human-readable String form of a difference in time in milliseconds.
*
* @param diff
- * The difference of time in milliseconds
+ * The difference of time in milliseconds
* @return The human-readable string form of the difference in time
*/
public static String getDiffTime(long diff) {
@@ -1453,7 +1475,8 @@ public static String getDiffTime(long diff) {
long diffDays = diff / (24 * 60 * 60 * 1000);
StringBuffer sb = new StringBuffer();
- sb.append(diffDays).append(" days ").append(diffHours).append(" hours ").append(diffMinutes).append(" minutes ").append(diffSeconds).append(" seconds");
+ sb.append(diffDays).append(" days ").append(diffHours).append(" hours ").append(diffMinutes).append(" minutes ")
+ .append(diffSeconds).append(" seconds");
return sb.toString();
}
@@ -1500,9 +1523,9 @@ public static String getBranch() {
* Install all services
*
* @throws ParseException
- * Unknown
+ * Unknown
* @throws IOException
- * Unknown
+ * Unknown
*/
// TODO: Check throws list to see if these are still thrown
static public void install() throws ParseException, IOException {
@@ -1513,7 +1536,7 @@ static public void install() throws ParseException, IOException {
* Install specified service.
*
* @param serviceType
- * Service to install
+ * Service to install
*/
static public void install(String serviceType) {
install(serviceType, null);
@@ -1533,9 +1556,9 @@ static public void install(String serviceType) {
* License - should be appropriately accepted or rejected by user
*
* @param serviceType
- * the service tyype to install
+ * the service tyype to install
* @param blocking
- * if this should block until done.
+ * if this should block until done.
*
*/
synchronized static public void install(String serviceType, Boolean blocking) {
@@ -1576,7 +1599,7 @@ public void run() {
* method.
*
* @param invoke
- * The array of service name, method, and parameters
+ * The array of service name, method, and parameters
*/
static public void invokeCommands(String[] invoke) {
@@ -1602,7 +1625,7 @@ static public void invokeCommands(String[] invoke) {
* Checks if a service is local to this MRL instance. The service must exist.
*
* @param serviceName
- * The name of the service to check
+ * The name of the service to check
* @return Whether the specified service is local or not
*/
public static boolean isLocal(String serviceName) {
@@ -1638,9 +1661,9 @@ public void startInteractiveMode() {
* handle it differently Windows Java updates have broken it several times
*
* @param in
- * The input stream to take commands from
+ * The input stream to take commands from
* @param out
- * The output stream to print command output to
+ * The output stream to print command output to
* @return The constructed CLI processor
*/
public InProcessCli startInteractiveMode(InputStream in, OutputStream out) {
@@ -1684,7 +1707,7 @@ static void mainHelp() {
* Logs a string message and publishes the message.
*
* @param msg
- * The message to log and publish
+ * The message to log and publish
* @return msg
*/
public static String message(String msg) {
@@ -1697,14 +1720,15 @@ public static String message(String msg) {
* Listener for state publishing, updates registry
*
* @param updatedService
- * Updated service to put in the registry
+ * Updated service to put in the registry
*/
public void onState(ServiceInterface updatedService) {
log.info("runtime updating registry info for remote service {}", updatedService.getName());
registry.put(String.format("%s@%s", updatedService.getName(), updatedService.getId()), updatedService);
}
- public static synchronized Registration register(String id, String name, String typeKey, ArrayList interfaces) {
+ public static synchronized Registration register(String id, String name, String typeKey,
+ ArrayList interfaces) {
Registration proxy = new Registration(id, name, typeKey, interfaces);
register(proxy);
return proxy;
@@ -1727,7 +1751,7 @@ public static synchronized Registration register(String id, String name, String
* re-broadcasting based on configuration
*
* @param registration
- * registration
+ * registration
* @return registration
*
*/
@@ -1750,7 +1774,8 @@ public static synchronized Registration register(Registration registration) {
// return null;
// }
- log.info("{}@{} registering at {} of type {}", registration.getName(), registration.getId(), Platform.getLocalInstance().getId(), registration.getTypeKey());
+ log.info("{}@{} registering at {} of type {}", registration.getName(), registration.getId(),
+ Platform.getLocalInstance().getId(), registration.getTypeKey());
if (!registration.isLocal(Platform.getLocalInstance().getId())) {
@@ -1768,10 +1793,12 @@ public static synchronized Registration register(Registration registration) {
// de-serialize, class exists
registration.service = Runtime.createService(registration.getName(), fullTypeName, registration.getId());
if (registration.getState() != null) {
- copyShallowFrom(registration.service, CodecUtils.fromJson(registration.getState(), Class.forName(fullTypeName)));
+ copyShallowFrom(registration.service,
+ CodecUtils.fromJson(registration.getState(), Class.forName(fullTypeName)));
}
} catch (ClassNotFoundException classNotFoundException) {
- log.error(String.format("Unknown service class for %s@%s: %s", registration.getName(), registration.getId(), registration.getTypeKey()), classNotFoundException);
+ log.error(String.format("Unknown service class for %s@%s: %s", registration.getName(), registration.getId(),
+ registration.getTypeKey()), classNotFoundException);
return null;
}
} else {
@@ -1784,7 +1811,8 @@ public static synchronized Registration register(Registration registration) {
// Interfaces should always include ServiceInterface if coming from
// remote client
if (registration.interfaces == null || registration.interfaces.isEmpty()) {
- log.error("Unknown service type being registered, registration does not contain any " + "interfaces for proxy generation: " + registration.getTypeKey());
+ log.error("Unknown service type being registered, registration does not contain any "
+ + "interfaces for proxy generation: " + registration.getTypeKey());
return null;
}
@@ -1901,7 +1929,7 @@ public static synchronized Registration register(Registration registration) {
* FIXME - clean up subscriptions from released
*
* @param inName
- * name to release
+ * name to release
* @return true/false
*
*/
@@ -1964,7 +1992,7 @@ public synchronized static boolean releaseService(String inName) {
* {@link #typeToInterface} and {@link #interfaceToNames}.
*
* @param inName
- * Name of the service to unregister
+ * Name of the service to unregister
*/
synchronized public static void unregister(String inName) {
String name = getFullName(inName);
@@ -2036,7 +2064,7 @@ public List getRemoteServices() {
* Get remote services associated with the MRL instance that has the given ID.
*
* @param id
- * The id of the target MRL instance
+ * The id of the target MRL instance
* @return A list of services running on the target instance
*/
public List getRemoteServices(String id) {
@@ -2079,7 +2107,7 @@ public static void releaseAll() {
* local only? YES !!! LOCAL ONLY !!
*
* @param releaseRuntime
- * Whether the Runtime should also be released
+ * Whether the Runtime should also be released
*/
public static void releaseAll(boolean releaseRuntime, boolean block) {
// a command thread is issuing this command is most likely
@@ -2107,7 +2135,7 @@ public void run() {
* Releases all threads and can be executed in a separate thread.
*
* @param releaseRuntime
- * Whether the Runtime should also be released
+ * Whether the Runtime should also be released
*/
synchronized static private void processRelease(boolean releaseRuntime) {
@@ -2155,7 +2183,7 @@ synchronized static private void processRelease(boolean releaseRuntime) {
* Shuts down this instance after the given number of seconds.
*
* @param seconds
- * sets task to shutdown in (n) seconds
+ * sets task to shutdown in (n) seconds
*/
// Why is this using the wrapper type? Null can be passed in and cause NPE
public static void shutdown(Integer seconds) {
@@ -2247,7 +2275,7 @@ public List publishConfigList() {
* given set
*
* @param saveMe
- * The set of services that should not be released
+ * The set of services that should not be released
*/
public static void releaseAllServicesExcept(HashSet saveMe) {
log.info("releaseAllServicesExcept");
@@ -2266,7 +2294,7 @@ public static void releaseAllServicesExcept(HashSet saveMe) {
* from registries.
*
* @param fullName
- * full name The service to be released
+ * full name The service to be released
*
*/
static public void release(String fullName) {
@@ -2277,7 +2305,7 @@ static public void release(String fullName) {
* Disconnect from remote process. FIXME - not implemented
*
* @throws IOException
- * Unknown
+ * Unknown
*/
// FIXME - implement ! also implement the callback events .. onDisconnect
public void disconnect() throws IOException {
@@ -2290,7 +2318,7 @@ public void disconnect() throws IOException {
* the cli
*
* @param id
- * instance id.
+ * instance id.
* @return string
*
*/
@@ -2325,9 +2353,9 @@ public String exit() {
* Send a command to the {@link InProcessCli}.
*
* @param srcFullName
- * Unknown
+ * Unknown
* @param cmd
- * The command to execute
+ * The command to execute
*/
public void sendToCli(String srcFullName, String cmd) {
Connection c = getConnection(stdCliUuid);
@@ -2351,9 +2379,10 @@ public void sendToCli(String srcFullName, String cmd) {
* FIXME implement autoReconnect
*
* @param url
- * The URL to connect to
+ * The URL to connect to
* @param autoReconnect
- * Whether the connection should be re-established if it is dropped
+ * Whether the connection should be re-established if it is
+ * dropped
*/
// FIXME - implement
public void connect(String url, boolean autoReconnect) {
@@ -2390,7 +2419,7 @@ public void checkConnections() {
* Connect to the MRL instance at the given URL
*
* @param url
- * Where the MRL instance being connected to is located
+ * Where the MRL instance being connected to is located
*/
@Override
public void connect(String url) {
@@ -2461,9 +2490,9 @@ public void connect(String url) {
* outgoing
*
* @param uuid
- * - connection for incoming data
+ * - connection for incoming data
* @param data
- * Incoming message in JSON String form
+ * Incoming message in JSON String form
*/
@Override // uuid
public void onRemoteMessage(String uuid, String data) {
@@ -2487,7 +2516,8 @@ public void onRemoteMessage(String uuid, String data) {
msg.setProperty("uuid", uuid); // Properties ???? REMOVE ???
if (msg.containsHop(getId())) {
- log.error("{} dumping duplicate hop msg to avoid cyclical from {} --to--> {}.{} | {}", getName(), msg.sender, msg.name, msg.method, msg.getHops());
+ log.error("{} dumping duplicate hop msg to avoid cyclical from {} --to--> {}.{} | {}", getName(), msg.sender,
+ msg.name, msg.method, msg.getHops());
return;
}
@@ -2544,11 +2574,11 @@ public void onRemoteMessage(String uuid, String data) {
* Add a route to the route table
*
* @param remoteId
- * Id of the remote instance
+ * Id of the remote instance
* @param uuid
- * Unknown
+ * Unknown
* @param metric
- * Unknown
+ * Unknown
* @see RouteTable#addRoute(String, String, int)
*/
public void addRoute(String remoteId, String uuid, int metric) {
@@ -2559,7 +2589,7 @@ public void addRoute(String remoteId, String uuid, int metric) {
* Start Runtime with the specified config
*
* @param configName
- * The name of the config file
+ * The name of the config file
*/
static public void startConfig(String configName) {
setConfig(configName);
@@ -2637,9 +2667,9 @@ public String publishConfigFinished(String configName) {
* Start a service of the specified type as the specified name.
*
* @param name
- * The name of the new service
+ * The name of the new service
* @param type
- * The type of the new service
+ * The type of the new service
* @return The started service
*/
synchronized static public ServiceInterface start(String name, String type) {
@@ -2661,7 +2691,8 @@ synchronized static public ServiceInterface start(String name, String type) {
Map services = createServicesFromPlan(plan, null, name);
if (services == null) {
- Runtime.getInstance().error("cannot create instance of %s with type %s given current configuration", name, type);
+ Runtime.getInstance().error("cannot create instance of %s with type %s given current configuration", name,
+ type);
return null;
}
@@ -2755,9 +2786,9 @@ synchronized public static Plan load(String name, String type) {
* TODO Check if there's a way to remove the assumptions about Runtime's name
*
* @param n
- * Name of the runtime. Should always be {@code "runtime"}
+ * Name of the runtime. Should always be {@code "runtime"}
* @param id
- * The ID of the instance this runtime belongs to.
+ * The ID of the instance this runtime belongs to.
*/
public Runtime(String n, String id) {
super(n, id);
@@ -2849,7 +2880,8 @@ public Runtime(String n, String id) {
log.info("pid {}", platform.getPid());
log.info("hostname {}", platform.getHostname());
log.info("ivy [runtime,{}.{}.{}]", platform.getArch(), platform.getJvmBitness(), platform.getOS());
- log.info("version {} branch {} commit {} build {}", platform.getVersion(), platform.getBranch(), platform.getCommit(), platform.getBuild());
+ log.info("version {} branch {} commit {} build {}", platform.getVersion(), platform.getBranch(),
+ platform.getCommit(), platform.getBuild());
log.info("platform manifest {}", Platform.getManifest());
log.info("platform [{}}]", platform);
log.info("version [{}]", platform.getVersion());
@@ -2944,7 +2976,7 @@ public void checkingForUpdates() {
* does not have any more tokens, returns an empty string instead.
*
* @param is
- * The input stream to read from
+ * The input stream to read from
* @return The entire input stream read as a string
*/
static public String getInputAsString(InputStream is) {
@@ -2966,7 +2998,7 @@ public Object ls() {
* List the contents of an absolute path.
*
* @param path
- * The path to list
+ * The path to list
* @return The contents of the directory
*/
public Object ls(String path) {
@@ -2983,9 +3015,9 @@ public Object ls(String path) {
* change the signature to match.
*
* @param contextPath
- * c
+ * c
* @param path
- * p
+ * p
* @return object
*
*/
@@ -3053,8 +3085,9 @@ public String whoami() {
* the exit value for the subprocess.
*
* @param program
- * The name of or path to an executable program. If given a name, the
- * program must be on the system PATH.
+ * The name of or path to an executable program. If given a name,
+ * the
+ * program must be on the system PATH.
* @return The exit value of the subprocess
*/
static public String exec(String program) {
@@ -3105,7 +3138,7 @@ public String[] getServiceTypeNames() {
* criteria
*
* @param filter
- * f
+ * f
* @return array of service types
*
*/
@@ -3121,13 +3154,14 @@ public String[] getServiceTypeNames(String filter) {
* account
*
* @param userId
- * Name of the MRL website account to link the log to
+ * Name of the MRL website account to link the log to
* @return Whether the log was sent successfully, info if yes and error if no.
*/
static public Status noWorky(String userId) {
Status status = null;
try {
- String retStr = HttpRequest.postFile("http://myrobotlab.org/myrobotlab_log/postLogFile.php", userId, "file", new File(LoggingFactory.getLogFileName()));
+ String retStr = HttpRequest.postFile("http://myrobotlab.org/myrobotlab_log/postLogFile.php", userId, "file",
+ new File(LoggingFactory.getLogFileName()));
if (retStr.contains("Upload:")) {
log.info("noWorky successfully sent - our crack team of experts will check it out !");
status = Status.info("no worky sent");
@@ -3169,7 +3203,8 @@ public void onMessage(Message msg) {
* re-broadcasting published registrations
*
* @param registration
- * - contains all the information need for a registration to process
+ * - contains all the information need for a registration to
+ * process
*/
@Override
public Registration registered(Registration registration) {
@@ -3191,10 +3226,10 @@ public String released(String name) {
* exists save the "default" config of that type of service
*
* @param name
- * name of service to export
+ * name of service to export
* @return true/false
* @throws IOException
- * boom
+ * boom
*
*/
@Deprecated /* use save(name) */
@@ -3302,7 +3337,7 @@ static public Map getManifest() {
* service - it will only set that Service type's log level
*
* @param level
- * - DEBUG | INFO | WARN | ERROR
+ * - DEBUG | INFO | WARN | ERROR
* @return the level which was set
*/
static public String setLogLevel(String level) {
@@ -3329,7 +3364,7 @@ static public String getLogLevel() {
* appenders from the logging system.
*
* @param file
- * The file to output logs to
+ * The file to output logs to
* @return file
* @see Logging#removeAllAppenders()
*/
@@ -3474,8 +3509,8 @@ public static Runtime get() {
* "https://superuser.com/questions/284342/what-are-path-and-other-environment-variables-and-how-can-i-set-or-use-them">
* What are PATH and other environment variables?
* @param args
- * The program to be executed as the first element and the args to
- * the program as the rest, if any
+ * The program to be executed as the first element and the args to
+ * the program as the rest, if any
* @return The program's stdout and stderr output
*/
static public String execute(String... args) {
@@ -3504,19 +3539,20 @@ static public String execute(String... args) {
* TODO Implement workingDir and block
*
* @param program
- * The program to be executed
+ * The program to be executed
* @param args
- * Any arguments to the command
+ * Any arguments to the command
* @param workingDir
- * The directory to execute the program in
+ * The directory to execute the program in
* @param additionalEnv
- * Any additional environment variables
+ * Any additional environment variables
* @param block
- * Whether this method blocks for the program to execute
+ * Whether this method blocks for the program to execute
* @return The programs stderr and stdout output
*/
- static public String execute(String program, List args, String workingDir, Map additionalEnv, boolean block) {
+ static public String execute(String program, List args, String workingDir, Map additionalEnv,
+ boolean block) {
log.debug("execToString(\"{} {}\")", program, args);
List command = new ArrayList<>();
@@ -3684,7 +3720,7 @@ public Map getLocales() {
* Set the locales by passing a list of locale IDs.
*
* @param codes
- * A list of locale IDs
+ * A list of locale IDs
* @return A map between the IDs and the Locale instances.
*/
public Map setLocales(String... codes) {
@@ -3706,11 +3742,11 @@ static public Security getSecurity() {
* {@link java.lang.Runtime#exec(String[])}.
*
* @param cmd
- * A list with the program name as the first element and any
- * arguments as the subsequent elements.
+ * A list with the program name as the first element and any
+ * arguments as the subsequent elements.
* @return The Process spawned by the execution
* @throws IOException
- * if an I/O error occurs while spawning the process
+ * if an I/O error occurs while spawning the process
*/
public static Process exec(String... cmd) throws IOException {
// FIXME - can't return a process - it will explode in serialization
@@ -3733,7 +3769,7 @@ public static CmdOptions getOptions() {
* TODO Unimplemented
*
* @param sd
- * ServiceData to use
+ * ServiceData to use
* @return sd
*/
public ServiceData setServiceTypes(ServiceData sd) {
@@ -3753,11 +3789,11 @@ public ServiceData setServiceTypes(ServiceData sd) {
* onRegistered system
*
* @param type
- * t
+ * t
* @param id
- * i
+ * i
* @param remoteUuid
- * remote id
+ * remote id
* @return describe results
*
*/
@@ -3787,9 +3823,9 @@ public DescribeResults describe() {
* FIXME uuid and query are unused
*
* @param uuid
- * u
+ * u
* @param query
- * q
+ * q
* @return describe results
*
*
@@ -3835,7 +3871,7 @@ public DescribeResults describe(String uuid, DescribeQuery query) {
* Describe results from remote query to describe
*
* @param results
- * describe results
+ * describe results
*
*
*/
@@ -3901,8 +3937,8 @@ public void onRegistered(Registration registration) {
* Listener for authentication.
*
* @param response
- * The results from a foreign instance's
- * {@link Runtime#describe(String, DescribeQuery)}
+ * The results from a foreign instance's
+ * {@link Runtime#describe(String, DescribeQuery)}
*/
public void onAuthenticate(DescribeResults response) {
log.info("onAuthenticate {}", response);
@@ -3928,11 +3964,11 @@ public List getServiceTypes() {
* Register a connection route from one instance to this one.
*
* @param uuid
- * Unique ID for a connecting client
+ * Unique ID for a connecting client
* @param id
- * Name or ID of the connecting client
+ * Name or ID of the connecting client
* @param connection
- * Details of the connection
+ * Details of the connection
*/
@Override
public void addConnection(String uuid, String id, Connection connection) {
@@ -3954,7 +3990,7 @@ public void addConnection(String uuid, String id, Connection connection) {
* Unregister all connections that a specified client has made.
*
* @param uuid
- * The ID of the client
+ * The ID of the client
*/
@Override
public void removeConnection(String uuid) {
@@ -3977,7 +4013,7 @@ public void removeConnection(String uuid) {
* Unregister all services originating from the instance with the given ID.
*
* @param id
- * The ID of the instance that is being unregistered
+ * The ID of the instance that is being unregistered
*/
public void unregisterId(String id) {
Set names = new HashSet<>(registry.keySet());
@@ -4011,7 +4047,7 @@ public Map getConnections() {
* for a specific connections connected clients
*
* @param gatwayName
- * name
+ * name
* @return map of connections
*
*/
@@ -4039,7 +4075,7 @@ public Map lc() {
* get a specific clients data
*
* @param uuid
- * uuid to get
+ * uuid to get
* @return connection for uuid
*
*/
@@ -4059,7 +4095,7 @@ public List getConnectionUuids() {
* Get whether a connection to the given client exists.
*
* @param uuid
- * Unique ID of the client to check for
+ * Unique ID of the client to check for
* @return Whether a connection between this instance and the given client
* exists
*/
@@ -4071,7 +4107,7 @@ boolean connectionExists(String uuid) {
* Get connection ids that belong to a specific gateway
*
* @param name
- * n
+ * n
* @return list of uuids
*
*/
@@ -4091,7 +4127,7 @@ public List getConnectionUuids(String name) {
* Get the Class instance for a specific service.
*
* @param inName
- * The name of the service
+ * The name of the service
* @return The Class of the service.
* @see #getFullName(String)
*/
@@ -4108,7 +4144,7 @@ public static Class> getClass(String inName) {
* takes an id returns a connection uuid
*
* @param id
- * id
+ * id
* @return the connection
*
*/
@@ -4124,7 +4160,7 @@ public RouteTable getRouteTable() {
* get gateway based on remote address of a msg e.g. msg.getRemoteId()
*
* @param remoteId
- * remote
+ * remote
* @return the gateway
*
*/
@@ -4163,7 +4199,7 @@ public Gateway getGatway(String remoteId) {
*
*
* @param shortname
- * The name to convert to a full name
+ * The name to convert to a full name
* @return shortname if it is already a full name, or a newly constructed full
* name
*/
@@ -4298,9 +4334,9 @@ public String stopped(String name) {
* Wrapper for {@link ServiceData#getMetaData(String, String)}
*
* @param serviceName
- * The name of the service
+ * The name of the service
* @param serviceType
- * The type of the service
+ * The type of the service
* @return The metadata of the service.
*/
public static MetaData getMetaData(String serviceName, String serviceType) {
@@ -4311,7 +4347,7 @@ public static MetaData getMetaData(String serviceName, String serviceType) {
* Wrapper for {@link ServiceData#getMetaData(String)}
*
* @param serviceType
- * The type of the service
+ * The type of the service
* @return The metadata of the service.
*/
public static MetaData getMetaData(String serviceType) {
@@ -4459,7 +4495,7 @@ public void python() {
* f-Dhttp.proxyPort=80 -Dhttps.proxyHost=webproxy -Dhttps.proxyPort=80
*
* @param args
- * cmd line args from agent spawn
+ * cmd line args from agent spawn
*
*/
public static void main(String[] args) {
@@ -4608,9 +4644,9 @@ public Connection getConnectionFromId(String remoteId) {
* "Connection" to send the msg remotely
*
* @param string
- * s
+ * s
* @param uuid
- * u
+ * u
*
*/
public void addLocalGatewayKey(String string, String uuid) {
@@ -4629,9 +4665,9 @@ public String getConnectionUuidFromGatewayKey(String gatewayKey) {
* This helper method will create, load then start a service
*
* @param name
- * - name of instance
+ * - name of instance
* @param type
- * - type
+ * - type
* @return returns the service in the form of a ServiceInterface
*/
static public ServiceInterface loadAndStart(String name, String type) {
@@ -4666,21 +4702,22 @@ static public ServiceInterface loadAndStart(String name, String type) {
*
*
* @param plan
- * - plan to load
+ * - plan to load
* @param name
- * - name of service
+ * - name of service
* @param type
- * - type of service
+ * - type of service
* @param start
- * - weather to specify in RuntimeConfig.registry to "start" this
- * service when createFromPlan is run
+ * - weather to specify in RuntimeConfig.registry to "start" this
+ * service when createFromPlan is run
* @param level
- * - level of the depth, services may load peers which in turn will
- * load more, this is the depth of recursion
+ * - level of the depth, services may load peers which in turn will
+ * load more, this is the depth of recursion
* @return
* @throws IOException
*/
- synchronized public Plan loadService(Plan plan, String name, String type, boolean start, int level) throws IOException {
+ synchronized public Plan loadService(Plan plan, String name, String type, boolean start, int level)
+ throws IOException {
if (plan == null) {
log.error("plan required to load a system");
@@ -4794,9 +4831,9 @@ public ServiceConfig readServiceConfig(String configName, String name) {
/**
*
* @param configName
- * - filename or dir of config set
+ * - filename or dir of config set
* @param name
- * - name of config file within that dir e.g. {name}.yml
+ * - name of config file within that dir e.g. {name}.yml
* @return
*/
public C readServiceConfig(String configName, String name, StaticType configType) {
@@ -4897,7 +4934,7 @@ static public void releaseConfig(String configName) {
* originally started services
*
* @param configPath
- * config set to release
+ * config set to release
*
*/
static public void releaseConfigPath(String configPath) {
@@ -4946,11 +4983,11 @@ static public boolean saveConfig(String configName) {
* will error
*
* @param configName
- * - config set name if null defaults to default
+ * - config set name if null defaults to default
* @param serviceName
- * - service name if null defaults to saveAll
+ * - service name if null defaults to saveAll
* @param filename
- * - if not explicitly set - will be standard yml filename
+ * - if not explicitly set - will be standard yml filename
* @return - true if all goes well
*/
public boolean saveService(String configName, String serviceName, String filename) {
@@ -5037,7 +5074,7 @@ public boolean isProcessingConfig() {
* the same way as all the other common static service methods
*
* @param configName
- * - config dir name under data/config/{config}
+ * - config dir name under data/config/{config}
* @return configName
*/
public static String setConfig(String configName) {
@@ -5070,8 +5107,10 @@ public void registerForInterfaceChange(String requestor, Class> interestedInte
* This data should be published whenever new "Type" definitions are found
*
* @param targetedInterface
- * - interface this add new interface to requested interfaces - add
- * current names of services which fulfill that interface "IS ASKING"
+ * - interface this add new interface to requested
+ * interfaces - add
+ * current names of services which fulfill that
+ * interface "IS ASKING"
*
*/
public void registerForInterfaceChange(String targetedInterface) {
@@ -5124,7 +5163,7 @@ static public Plan saveDefault(String className) {
* Helper method - returns if a service is started
*
* @param name
- * - name of service
+ * - name of service
* @return - true if started
*/
static public boolean isStarted(String name) {
@@ -5149,7 +5188,7 @@ static public boolean isStarted(String name) {
* Load all configuration files from a given directory.
*
* @param configPath
- * The directory to load from
+ * The directory to load from
*/
public static void loadConfigPath(String configPath) {
@@ -5178,8 +5217,8 @@ public static void loadConfigPath(String configPath) {
* Load a service from a file
*
* @param path
- * The full path of the file to load - this DOES NOT set the
- * configPath
+ * The full path of the file to load - this DOES NOT set the
+ * configPath
*/
public void loadFile(String path) {
try {
@@ -5250,7 +5289,8 @@ public void saveAllDefaults() {
public void saveAllDefaults(String configPath, boolean fullPlan) {
List types = serviceData.getAvailableServiceTypes();
for (MetaData meta : types) {
- saveDefault(configPath + fs + meta.getSimpleName(), meta.getSimpleName().toLowerCase(), meta.getSimpleName(), fullPlan);
+ saveDefault(configPath + fs + meta.getSimpleName(), meta.getSimpleName().toLowerCase(), meta.getSimpleName(),
+ fullPlan);
}
}
@@ -5375,7 +5415,8 @@ public ServiceConfig getPeer(String sericeName, String peerKey) {
/**
* Removes a config set and all its files
*
- * @param configName - name of config
+ * @param configName
+ * - name of config
*/
public static void removeConfig(String configName) {
try {
diff --git a/src/main/java/org/myrobotlab/service/Serial.java b/src/main/java/org/myrobotlab/service/Serial.java
index af16f3522a..ca48f2f7cc 100644
--- a/src/main/java/org/myrobotlab/service/Serial.java
+++ b/src/main/java/org/myrobotlab/service/Serial.java
@@ -1291,6 +1291,19 @@ public void startTcpServer(Integer port) throws IOException {
public void stopTcpServer() throws IOException {
tcpSerialHub.stop();
}
+
+ public void startService() {
+ super.startService();
+ if (config.port != null) {
+ try {
+ if (!isConnected()) {
+ connect(config.port);
+ }
+ } catch (Exception e) {
+ log.error("load connecting threw", e);
+ }
+ }
+ }
@Override
public SerialConfig getConfig() {
@@ -1300,21 +1313,6 @@ public SerialConfig getConfig() {
return config;
}
- @Override
- public SerialConfig apply(SerialConfig c) {
- super.apply(c);
-
- if (c.port != null) {
- try {
- if (isConnected()) {
- connect(c.port);
- }
- } catch (Exception e) {
- log.error("load connecting threw", e);
- }
- }
- return c;
- }
public static void main(String[] args) {
diff --git a/src/main/java/org/myrobotlab/service/ServoMixer.java b/src/main/java/org/myrobotlab/service/ServoMixer.java
index 8963ceeed1..c6b9a2285b 100755
--- a/src/main/java/org/myrobotlab/service/ServoMixer.java
+++ b/src/main/java/org/myrobotlab/service/ServoMixer.java
@@ -677,7 +677,7 @@ public void saveGesture(String filename, Gesture gesture) {
error("save gesture file name cannot be null");
return;
}
-
+
if (gesture == null) {
log.info("creating empty gesture");
gesture = new Gesture();
diff --git a/src/main/java/org/myrobotlab/service/SlackBot.java b/src/main/java/org/myrobotlab/service/SlackBot.java
deleted file mode 100755
index 990fc21855..0000000000
--- a/src/main/java/org/myrobotlab/service/SlackBot.java
+++ /dev/null
@@ -1,148 +0,0 @@
-package org.myrobotlab.service;
-
-import java.io.IOException;
-
-import org.myrobotlab.framework.Service;
-import org.myrobotlab.logging.LoggerFactory;
-import org.myrobotlab.logging.LoggingFactory;
-import org.myrobotlab.service.config.ServiceConfig;
-import org.myrobotlab.service.config.SlackBotConfig;
-import org.myrobotlab.service.data.Utterance;
-import org.myrobotlab.service.interfaces.UtteranceListener;
-import org.myrobotlab.service.interfaces.UtterancePublisher;
-import org.slf4j.Logger;
-
-import com.slack.api.Slack;
-import com.slack.api.bolt.App;
-import com.slack.api.bolt.AppConfig;
-import com.slack.api.bolt.socket_mode.SocketModeApp;
-import com.slack.api.methods.MethodsClient;
-import com.slack.api.methods.SlackApiException;
-import com.slack.api.methods.response.chat.ChatPostMessageResponse;
-import com.slack.api.model.event.AppMentionEvent;
-import com.slack.api.model.event.MessageEvent;
-
-/**
- * A slack bot gateway for utterance publishers and listeners.
- *
- */
-public class SlackBot extends Service implements UtteranceListener, UtterancePublisher {
-
- public final static Logger log = LoggerFactory.getLogger(SlackBot.class);
-
- private static final long serialVersionUID = 1L;
-
- // something like "xoxb-XXXXXXXXX-XXXXXXXX-XXXXXXXXXXXXX"
- String botToken;
- // something like
- // "xapp-X-XXXXXXXX-XXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
- String appToken;
-
- public SlackBot(String reservedKey, String inId) {
- super(reservedKey, inId);
- }
-
- @Override
- public SlackBotConfig getConfig() {
- super.getConfig();
- // FIXME remove members and use config only
- config.appToken = appToken;
- config.botToken = botToken;
- return config;
- }
-
- @Override
- public SlackBotConfig apply(SlackBotConfig c) {
- SlackBotConfig config = (SlackBotConfig) super.apply(c);
- appToken = config.appToken;
- botToken = config.botToken;
- // TODO: should we connect here?
- return config;
- }
-
- public void connect() throws IOException, Exception {
- // Connect to slack!
- AppConfig appConfig = AppConfig.builder().singleTeamBotToken(botToken).build();
- App app = new App(appConfig);
- // some callbacks..??
- app.event(MessageEvent.class, (req, ctx) -> {
- log.info("Message Event Req: {} and Ctx: {}", req, ctx);
- // turn this message event into an utterance so we can publish it.
- Utterance utterance = new Utterance();
- utterance.text = req.getEvent().getText();
- utterance.id = req.getEventId();
- utterance.channel = req.getEvent().getChannel();
- utterance.channelType = req.getEvent().getChannelType();
- // TODO: this should be human readable?
- utterance.channelBotName = ctx.getBotUserId();
- utterance.username = req.getEvent().getUser();
- log.info("Utterance: {}", utterance);
- // publish it.
- invoke("publishUtterance", utterance);
- return ctx.ack();
- });
-
- app.event(AppMentionEvent.class, (req, ctx) -> {
- // We probably don't actually need to register this handler
- // for this event, the ProgramAB instance currently decides
- // if the utterance is intended for the bot...
- log.info("The bot was mentioned in a message.");
- return ctx.ack();
- });
-
- SocketModeApp socketModeApp = new SocketModeApp(appToken, app);
- // This does not block the current thread
- socketModeApp.startAsync();
- }
-
- @Override
- public Utterance publishUtterance(Utterance utterance) {
- // publish the utterance to the listerners.
- return utterance;
- }
-
- @Override
- public void onUtterance(Utterance utterance) throws Exception {
- // TODO: if ProgramAB or other utterance publisher sends us an utterance
- // we need to relay the message to the proper slack channel
- log.info("On Utterance: {}", utterance);
- // send the message to the slack channel in the utterance.
- publishMessage(utterance.channel, utterance.text, botToken);
- }
-
- // helper method to publish a message to a slack channel.
- static void publishMessage(String id, String text, String botToken) {
- // you can get this instance via ctx.client() in a Bolt app
- MethodsClient client = Slack.getInstance().methods();
- try {
- // Call the chat.postMessage method using the built-in WebClient
- ChatPostMessageResponse result = client.chatPostMessage(r -> r
- // The token you used to initialize your app
- .token(botToken).channel(id).text(text)
- // You could also use a blocks[] array to send richer content
- );
- // Print result, which includes information about the message (like TS)
- log.info("result {}", result);
- } catch (IOException | SlackApiException e) {
- log.error("error: {}", e.getMessage(), e);
- }
- }
-
- public static void main(String[] args) throws IOException, Exception {
- LoggingFactory.getInstance().setLevel("INFO");
- SlackBot slackBot = (SlackBot) Runtime.start("slackBot", "SlackBot");
- // add your app / bot tokens here to authenticate as your bot user.
- String botToken = "xoxb-XXXXXXXX-XXXXXXXXX-XXXXXXXXXXXXXXXXXXXX";
- String appToken = "xapp-X-XXXXXXX-XXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
- slackBot.appToken = appToken;
- slackBot.botToken = botToken;
- // Let's see about getting a programAB instance setup and attached to the
- // slack bot
- ProgramAB chatBot = (ProgramAB) Runtime.start("chatBot", "ProgramAB");
- chatBot.setCurrentBotName("Mr. Turing");
- slackBot.attachUtteranceListener(chatBot.getName());
- chatBot.attachUtteranceListener(slackBot.getName());
- // Tell the slack bot to connect
- slackBot.connect();
- }
-}
diff --git a/src/main/java/org/myrobotlab/service/WebGui.java b/src/main/java/org/myrobotlab/service/WebGui.java
index e52e4dc5c5..8d75f0b08e 100644
--- a/src/main/java/org/myrobotlab/service/WebGui.java
+++ b/src/main/java/org/myrobotlab/service/WebGui.java
@@ -45,6 +45,7 @@
import org.myrobotlab.net.BareBonesBrowserLaunch;
import org.myrobotlab.net.Connection;
import org.myrobotlab.service.config.WebGuiConfig;
+import org.myrobotlab.service.data.Classification;
import org.myrobotlab.service.interfaces.AuthorizationProvider;
import org.myrobotlab.service.interfaces.Gateway;
import org.myrobotlab.service.interfaces.ServiceLifeCycleListener;
@@ -89,7 +90,7 @@ public void handle(AtmosphereResource r) {
}
}
}
-
+
private final transient IncomingMsgQueue inMsgQueue = new IncomingMsgQueue();
public static class Panel {
@@ -127,7 +128,7 @@ public Panel(String name, int x, int y, int z) {
* needed to get the api key to select the appropriate api processor
*
* @param uri
- * u
+ * u
* @return api key
*
*/
@@ -270,9 +271,9 @@ public boolean getAutoStartBrowser() {
* String broadcast to specific client
*
* @param uuid
- * u
+ * u
* @param str
- * s
+ * s
*
*/
public void broadcast(String uuid, String str) {
@@ -314,7 +315,9 @@ public Config.Builder getNettosphereConfig() {
// cert.privateKey()).build();
SelfSignedCertificate selfSignedCertificate = new SelfSignedCertificate();
- SslContext context = SslContextBuilder.forServer(selfSignedCertificate.certificate(), selfSignedCertificate.privateKey()).sslProvider(SslProvider.JDK)
+ SslContext context = SslContextBuilder
+ .forServer(selfSignedCertificate.certificate(), selfSignedCertificate.privateKey())
+ .sslProvider(SslProvider.JDK)
.clientAuth(ClientAuth.NONE).build();
configBuilder.sslContext(context);
@@ -915,7 +918,7 @@ public void run() {
* remotely control UI
*
* @param panel
- * - the panel which has been moved or resized
+ * - the panel which has been moved or resized
*/
public void savePanel(Panel panel) {
if (panel.name == null) {
@@ -1102,7 +1105,7 @@ public void releaseService() {
* Default (false) is to use the CDN
*
* @param useLocalResources
- * - true uses local resources fals uses cdn
+ * - true uses local resources fals uses cdn
*/
public void useLocalResources(boolean useLocalResources) {
this.useLocalResources = useLocalResources;
@@ -1162,7 +1165,7 @@ public WebGuiConfig getConfig() {
public WebGuiConfig apply(WebGuiConfig c) {
super.apply(c);
-
+
if (c.port != null && (port != null && c.port.intValue() != port.intValue())) {
setPort(c.port);
}
@@ -1178,8 +1181,10 @@ public static void main(String[] args) {
try {
- Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", "WebGui", "intro", "Intro", "python", "Python" });
-
+ // Runtime.startConfig("dev");
+ Runtime.main(new String[] { "--log-level", "info", "-s", "vertx", "Vertx", "intro", "Intro", "python", "Python" });
+ // Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", "WebGui", "intro", "Intro", "python", "Python" });
+ // Runtime.main(new String[] { "--log-level", "info", "-c", "dev" });
boolean done = true;
if (done) {
return;
diff --git a/src/main/java/org/myrobotlab/service/config/AudioFileConfig.java b/src/main/java/org/myrobotlab/service/config/AudioFileConfig.java
index 391d175d55..7e33ed97a0 100644
--- a/src/main/java/org/myrobotlab/service/config/AudioFileConfig.java
+++ b/src/main/java/org/myrobotlab/service/config/AudioFileConfig.java
@@ -4,7 +4,7 @@
import java.util.Map;
public class AudioFileConfig extends ServiceConfig {
-
+
public boolean mute = false;
public String currentTrack = "default";
public double volume = 1.0;
diff --git a/src/main/java/org/myrobotlab/service/config/CalikoConfig.java b/src/main/java/org/myrobotlab/service/config/CalikoConfig.java
new file mode 100644
index 0000000000..d1c3ff9554
--- /dev/null
+++ b/src/main/java/org/myrobotlab/service/config/CalikoConfig.java
@@ -0,0 +1,36 @@
+package org.myrobotlab.service.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.myrobotlab.service.Pid.PidData;
+
+public class CalikoConfig extends ServiceConfig {
+
+ public Map data = new HashMap<>();
+
+ public boolean use3dDemo;
+
+ public int demoNumber;
+
+ public boolean drawConstraints;
+
+ public Object rotateBasesMode;
+
+ public boolean drawLines;
+
+ public boolean drawModels;
+
+ public boolean fixedBaseMode;
+
+ public boolean drawAxes;
+
+ public boolean paused;
+
+ public boolean leftMouseButtonDown;
+
+ public int windowWidth;
+
+ public int windowHeight;
+
+}
diff --git a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java
index 564fe516e4..94b1917bfb 100644
--- a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java
+++ b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java
@@ -55,7 +55,7 @@ public class InMoov2Config extends ServiceConfig {
* fire events to the FSM. Checks battery level and sends a heartbeat flash on
* publishHeartbeat and onHeartbeat at a regular interval
*/
- public boolean heartbeat = false;
+ public boolean heartbeat = true;
/**
* flashes the neopixel every time a health check is preformed. green == good
@@ -253,7 +253,8 @@ public Plan getDefault(Plan plan, String name) {
chatBot.bots.add("resource/ProgramAB/tr-TR");
Runtime runtime = Runtime.getInstance();
- String[] bots = new String[] { "cn-ZH", "en-US", "fi-FI", "hi-IN", "nl-NL", "ru-RU", "de-DE", "es-ES", "fr-FR", "it-IT", "pt-PT", "tr-TR" };
+ String[] bots = new String[] { "cn-ZH", "en-US", "fi-FI", "hi-IN", "nl-NL", "ru-RU", "de-DE", "es-ES", "fr-FR",
+ "it-IT", "pt-PT", "tr-TR" };
String tag = runtime.getLocaleTag();
if (tag != null) {
String[] tagparts = tag.split("-");
@@ -294,68 +295,126 @@ public Plan getDefault(Plan plan, String name) {
JMonkeyEngineConfig simulator = (JMonkeyEngineConfig) plan.get(getPeerName("simulator"));
- simulator.multiMapped.put(name + ".leftHand.index", new String[] { name + ".leftHand.index", name + ".leftHand.index2", name + ".leftHand.index3" });
- simulator.multiMapped.put(name + ".leftHand.majeure", new String[] { name + ".leftHand.majeure", name + ".leftHand.majeure2", name + ".leftHand.majeure3" });
- simulator.multiMapped.put(name + ".leftHand.pinky", new String[] { name + ".leftHand.pinky", name + ".leftHand.pinky2", name + ".leftHand.pinky3" });
- simulator.multiMapped.put(name + ".leftHand.ringFinger", new String[] { name + ".leftHand.ringFinger", name + ".leftHand.ringFinger2", name + ".leftHand.ringFinger3" });
- simulator.multiMapped.put(name + ".leftHand.thumb", new String[] { name + ".leftHand.thumb1", name + ".leftHand.thumb2", name + ".leftHand.thumb3" });
-
- simulator.multiMapped.put(name + ".rightHand.index", new String[] { name + ".rightHand.index", name + ".rightHand.index2", name + ".rightHand.index3" });
- simulator.multiMapped.put(name + ".rightHand.majeure", new String[] { name + ".rightHand.majeure", name + ".rightHand.majeure2", name + ".rightHand.majeure3" });
- simulator.multiMapped.put(name + ".rightHand.pinky", new String[] { name + ".rightHand.pinky", name + ".rightHand.pinky2", name + ".rightHand.pinky3" });
- simulator.multiMapped.put(name + ".rightHand.ringFinger", new String[] { name + ".rightHand.ringFinger", name + ".rightHand.ringFinger2", name + ".rightHand.ringFinger3" });
- simulator.multiMapped.put(name + ".rightHand.thumb", new String[] { name + ".rightHand.thumb1", name + ".rightHand.thumb2", name + ".rightHand.thumb3" });
+ simulator.multiMapped.put(name + ".leftHand.index",
+ new String[] { name + ".leftHand.index", name + ".leftHand.index2", name + ".leftHand.index3" });
+ simulator.multiMapped.put(name + ".leftHand.majeure",
+ new String[] { name + ".leftHand.majeure", name + ".leftHand.majeure2", name + ".leftHand.majeure3" });
+ simulator.multiMapped.put(name + ".leftHand.pinky",
+ new String[] { name + ".leftHand.pinky", name + ".leftHand.pinky2", name + ".leftHand.pinky3" });
+ simulator.multiMapped.put(name + ".leftHand.ringFinger",
+ new String[] { name + ".leftHand.ringFinger", name + ".leftHand.ringFinger2", name + ".leftHand.ringFinger3" });
+ simulator.multiMapped.put(name + ".leftHand.thumb",
+ new String[] { name + ".leftHand.thumb1", name + ".leftHand.thumb2", name + ".leftHand.thumb3" });
+
+ simulator.multiMapped.put(name + ".rightHand.index",
+ new String[] { name + ".rightHand.index", name + ".rightHand.index2", name + ".rightHand.index3" });
+ simulator.multiMapped.put(name + ".rightHand.majeure",
+ new String[] { name + ".rightHand.majeure", name + ".rightHand.majeure2", name + ".rightHand.majeure3" });
+ simulator.multiMapped.put(name + ".rightHand.pinky",
+ new String[] { name + ".rightHand.pinky", name + ".rightHand.pinky2", name + ".rightHand.pinky3" });
+ simulator.multiMapped.put(name + ".rightHand.ringFinger", new String[] { name + ".rightHand.ringFinger",
+ name + ".rightHand.ringFinger2", name + ".rightHand.ringFinger3" });
+ simulator.multiMapped.put(name + ".rightHand.thumb",
+ new String[] { name + ".rightHand.thumb1", name + ".rightHand.thumb2", name + ".rightHand.thumb3" });
// simulator.nodes.put("camera", new UserData());
- simulator.nodes.put(name + ".head.jaw", new UserDataConfig(new MapperLinear(0.0, 180.0, -5.0, 80.0, true, false), "x"));
- simulator.nodes.put(name + ".head.neck", new UserDataConfig(new MapperLinear(0.0, 180.0, 20.0, -20.0, true, false), "x"));
+ simulator.nodes.put(name + ".head.jaw",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -5.0, 80.0, true, false), "x"));
+ simulator.nodes.put(name + ".head.neck",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 20.0, -20.0, true, false), "x"));
simulator.nodes.put(name + ".head.rothead", new UserDataConfig(null, "y"));
- simulator.nodes.put(name + ".head.rollNeck", new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -30.0, true, false), "z"));
- simulator.nodes.put(name + ".head.eyeY", new UserDataConfig(new MapperLinear(0.0, 180.0, 40.0, 140.0, true, false), "x"));
- simulator.nodes.put(name + ".head.eyeX", new UserDataConfig(new MapperLinear(0.0, 180.0, -10.0, 70.0, true, false), "y"));
- simulator.nodes.put(name + ".torso.topStom", new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, 30.0, true, false), "z"));
- simulator.nodes.put(name + ".torso.midStom", new UserDataConfig(new MapperLinear(0.0, 180.0, 50.0, 130.0, true, false), "y"));
- simulator.nodes.put(name + ".torso.lowStom", new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, 30.0, true, false), "x"));
- simulator.nodes.put(name + ".rightArm.bicep", new UserDataConfig(new MapperLinear(0.0, 180.0, 0.0, -150.0, true, false), "x"));
- simulator.nodes.put(name + ".leftArm.bicep", new UserDataConfig(new MapperLinear(0.0, 180.0, 0.0, -150.0, true, false), "x"));
- simulator.nodes.put(name + ".rightArm.shoulder", new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -150.0, true, false), "x"));
- simulator.nodes.put(name + ".leftArm.shoulder", new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -150.0, true, false), "x"));
- simulator.nodes.put(name + ".rightArm.rotate", new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, -80.0, true, false), "y"));
- simulator.nodes.put(name + ".leftArm.rotate", new UserDataConfig(new MapperLinear(0.0, 180.0, -80.0, 80.0, true, false), "y"));
- simulator.nodes.put(name + ".rightArm.omoplate", new UserDataConfig(new MapperLinear(0.0, 180.0, 10.0, -180.0, true, false), "z"));
- simulator.nodes.put(name + ".leftArm.omoplate", new UserDataConfig(new MapperLinear(0.0, 180.0, -10.0, 180.0, true, false), "z"));
- simulator.nodes.put(name + ".rightHand.wrist", new UserDataConfig(new MapperLinear(0.0, 180.0, -20.0, 60.0, true, false), "y"));
- simulator.nodes.put(name + ".leftHand.wrist", new UserDataConfig(new MapperLinear(0.0, 180.0, 20.0, -60.0, true, false), "y"));
- simulator.nodes.put(name + ".leftHand.thumb1", new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, -100.0, true, false), "y"));
- simulator.nodes.put(name + ".leftHand.thumb2", new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, 20.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.thumb3", new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, 20.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.index", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.index2", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.index3", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.majeure", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.majeure2", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.majeure3", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.ringFinger", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.ringFinger2", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.ringFinger3", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.pinky", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.pinky2", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".leftHand.pinky3", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.thumb1", new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, 110.0, true, false), "y"));
- simulator.nodes.put(name + ".rightHand.thumb2", new UserDataConfig(new MapperLinear(0.0, 180.0, -100.0, -150.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.thumb3", new UserDataConfig(new MapperLinear(0.0, 180.0, -100.0, -160.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.index", new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.index2", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.index3", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.majeure", new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.majeure2", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.majeure3", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.ringFinger", new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.ringFinger2", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.ringFinger3", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.pinky", new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.pinky2", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
- simulator.nodes.put(name + ".rightHand.pinky3", new UserDataConfig(new MapperLinear(0.0, 180.0, 60.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".head.rollNeck",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -30.0, true, false), "z"));
+ simulator.nodes.put(name + ".head.eyeY",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 40.0, 140.0, true, false), "x"));
+ simulator.nodes.put(name + ".head.eyeX",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -10.0, 70.0, true, false), "y"));
+ simulator.nodes.put(name + ".torso.topStom",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, 30.0, true, false), "z"));
+ simulator.nodes.put(name + ".torso.midStom",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 50.0, 130.0, true, false), "y"));
+ simulator.nodes.put(name + ".torso.lowStom",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, 30.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightArm.bicep",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 0.0, -150.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftArm.bicep",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 0.0, -150.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightArm.shoulder",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -150.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftArm.shoulder",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -150.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightArm.rotate",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, -80.0, true, false), "y"));
+ simulator.nodes.put(name + ".leftArm.rotate",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -80.0, 80.0, true, false), "y"));
+ simulator.nodes.put(name + ".rightArm.omoplate",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 10.0, -180.0, true, false), "z"));
+ simulator.nodes.put(name + ".leftArm.omoplate",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -10.0, 180.0, true, false), "z"));
+ simulator.nodes.put(name + ".rightHand.wrist",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -20.0, 60.0, true, false), "y"));
+ simulator.nodes.put(name + ".leftHand.wrist",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 20.0, -60.0, true, false), "y"));
+ simulator.nodes.put(name + ".leftHand.thumb1",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, -100.0, true, false), "y"));
+ simulator.nodes.put(name + ".leftHand.thumb2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, 20.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.thumb3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, 20.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.index",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.index2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.index3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.majeure",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.majeure2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.majeure3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.ringFinger",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.ringFinger2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.ringFinger3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.pinky",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.pinky2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".leftHand.pinky3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.thumb1",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, 110.0, true, false), "y"));
+ simulator.nodes.put(name + ".rightHand.thumb2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -100.0, -150.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.thumb3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, -100.0, -160.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.index",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.index2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.index3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.majeure",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.majeure2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.majeure3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.ringFinger",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.ringFinger2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.ringFinger3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.pinky",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.pinky2",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x"));
+ simulator.nodes.put(name + ".rightHand.pinky3",
+ new UserDataConfig(new MapperLinear(0.0, 180.0, 60.0, -10.0, true, false), "x"));
simulator.cameraLookAt = name + ".torso.lowStom";
FiniteStateMachineConfig fsm = (FiniteStateMachineConfig) plan.get(getPeerName("fsm"));
@@ -390,7 +449,8 @@ public Plan getDefault(Plan plan, String name) {
random.enabled = false;
// setup name references to different services
- RandomMessageConfig rm = new RandomMessageConfig(name, "setLeftArmSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0);
+ RandomMessageConfig rm = new RandomMessageConfig(name, "setLeftArmSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0,
+ 25.0, 8.0, 25.0);
random.randomMessages.put(name + ".setLeftArmSpeed", rm);
rm = new RandomMessageConfig(name, "setRightArmSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0);
@@ -402,16 +462,20 @@ public Plan getDefault(Plan plan, String name) {
rm = new RandomMessageConfig(name, "moveRightArm", 3000, 8000, 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0);
random.randomMessages.put(name + ".moveRightArm", rm);
- rm = new RandomMessageConfig(name, "setLeftHandSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0);
+ rm = new RandomMessageConfig(name, "setLeftHandSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0,
+ 25.0, 8.0, 25.0);
random.randomMessages.put(name + ".setLeftHandSpeed", rm);
- rm = new RandomMessageConfig(name, "setRightHandSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0);
+ rm = new RandomMessageConfig(name, "setRightHandSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0,
+ 25.0, 8.0, 25.0);
random.randomMessages.put(name + ".setRightHandSpeed", rm);
- rm = new RandomMessageConfig(name, "moveLeftHand", 3000, 8000, 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 130.0, 175.0);
+ rm = new RandomMessageConfig(name, "moveLeftHand", 3000, 8000, 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0,
+ 10.0, 60.0, 130.0, 175.0);
random.randomMessages.put(name + ".moveLeftHand", rm);
- rm = new RandomMessageConfig(name, "moveRightHand", 3000, 8000, 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 130.0, 175.0);
+ rm = new RandomMessageConfig(name, "moveRightHand", 3000, 8000, 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0,
+ 10.0, 60.0, 130.0, 175.0);
random.randomMessages.put(name + ".moveRightHand", rm);
rm = new RandomMessageConfig(name, "setHeadSpeed", 3000, 8000, 8.0, 20.0, 8.0, 20.0, 8.0, 20.0);
@@ -532,12 +596,11 @@ public Plan getDefault(Plan plan, String name) {
// service --to--> InMoov2
AudioFileConfig mouth_audioFile = (AudioFileConfig) plan.get(getPeerName("mouth.audioFile"));
mouth_audioFile.listeners.add(new Listener("publishPeak", name));
-
+
OakDConfig oakd = (OakDConfig) plan.get(getPeerName("oakd"));
oakd.listeners.add(new Listener("publishClassification", name));
oakd.getPeer("py4j").name = getPeerName("py4j");
-
webxr.listeners.add(new Listener("publishJointAngles", name));
// mouth_audioFile.listeners.add(new Listener("publishAudioEnd", name));
diff --git a/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java b/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java
index ce9ae14033..da20e94849 100644
--- a/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java
+++ b/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java
@@ -6,20 +6,17 @@
import org.myrobotlab.framework.Plan;
public class ProgramABConfig extends ServiceConfig {
-
- @Deprecated /* unused text filters */
- public String[] textFilters;
-
+
/**
- * a directory ProgramAB will scan for new bots
+ * a directory ProgramAB will scan for new bots on startup
*/
- public String botDir;
+ public String botDir = "resource/ProgramAB/";
/**
* explicit bot directories
*/
public List bots = new ArrayList<>();
-
+
/**
* current sessions bot name, it must match a botname that was scanned
* currently with ProgramAB Alice, Dr.Who, Mr. Turing and Ency
@@ -39,13 +36,25 @@ public class ProgramABConfig extends ServiceConfig {
* current sleep/wake value
*/
public boolean sleep = false;
-
+
/**
- * topic to start with, if null then topic will be loaded from predicates of
- * a new session if available, this means a config/{username}.predicates.txt
+ * topic to start with, if null then topic will be loaded from predicates of
+ * a new session if available, this means a
+ * config/{currentUserName}.predicates.txt
* will need to exist with a topic field
*/
- public String startTopic = "unknown";
+ public String startTopic = null;
+
+ /**
+ * bot will prompt users if enabled trolling is true
+ * after maxConversationDelay has passed
+ */
+ public boolean enableTrolling = false;
+
+ /**
+ * Number of milliseconds before the robot starts talking on its own.
+ */
+ public int maxConversationDelay = 5000;
@Override
public Plan getDefault(Plan plan, String name) {
diff --git a/src/main/java/org/myrobotlab/service/config/Py4jConfig.java b/src/main/java/org/myrobotlab/service/config/Py4jConfig.java
index 615dac7d2a..01c28faea2 100644
--- a/src/main/java/org/myrobotlab/service/config/Py4jConfig.java
+++ b/src/main/java/org/myrobotlab/service/config/Py4jConfig.java
@@ -4,7 +4,7 @@ public class Py4jConfig extends ServiceConfig {
/**
* root of python scripts - if not specified by user it will be
- * /data/Py4j/{serviceName}
+ * /data/Py4j/{serviceName}
*/
public String scriptRootDir;
@@ -13,7 +13,6 @@ public class Py4jConfig extends ServiceConfig {
* or invoke the system Python.
*/
public boolean useBundledPython = true;
-
/**
* Whether to start a new Python process with
* stdin/stdout connected to the JVM or leave the
diff --git a/src/main/java/org/myrobotlab/service/config/SlackBotConfig.java b/src/main/java/org/myrobotlab/service/config/SlackBotConfig.java
deleted file mode 100755
index 20aba609fe..0000000000
--- a/src/main/java/org/myrobotlab/service/config/SlackBotConfig.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.myrobotlab.service.config;
-
-public class SlackBotConfig extends ServiceConfig {
-
- // Slack bot api key and other stuff??
- public String botToken;
- public String appToken;
-
-}
diff --git a/src/main/java/org/myrobotlab/service/data/TopicChange.java b/src/main/java/org/myrobotlab/service/data/TopicChange.java
deleted file mode 100644
index af25c05f06..0000000000
--- a/src/main/java/org/myrobotlab/service/data/TopicChange.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.myrobotlab.service.data;
-
-/**
- * Purpose of this class is to be a simple data POJO
- * for ProgramAB topic changes. This will be useful to
- * interface the state machine implemented in AIML where
- * topic changes represent changes of state.
- *
- * @author GroG
- *
- */
-public class TopicChange {
-
- /**
- * previous topic or state in this state transition
- */
- public String oldTopic;
-
- /**
- * new topic or state name in this transition
- */
- public String newTopic;
-
- /**
- * the user name in this state change - usually
- * current session userName
- */
- public String userName;
-
- /**
- * the botName in this state change - typically
- * current session botName
- */
- public String botName;
-
- public TopicChange(String userName, String botName, String newTopic, String oldTopic) {
- this.userName = userName;
- this.botName = botName;
- this.newTopic = newTopic;
- this.oldTopic = oldTopic;
- }
-
-
-}
diff --git a/src/main/java/org/myrobotlab/service/meta/CalikoMeta.java b/src/main/java/org/myrobotlab/service/meta/CalikoMeta.java
new file mode 100644
index 0000000000..5e98d56744
--- /dev/null
+++ b/src/main/java/org/myrobotlab/service/meta/CalikoMeta.java
@@ -0,0 +1,41 @@
+package org.myrobotlab.service.meta;
+
+import org.myrobotlab.logging.LoggerFactory;
+import org.myrobotlab.service.meta.abstracts.MetaData;
+import org.slf4j.Logger;
+
+public class CalikoMeta extends MetaData {
+ private static final long serialVersionUID = 1L;
+ public final static Logger log = LoggerFactory.getLogger(CalikoMeta.class);
+
+ /**
+ * This class is contains all the meta data details of a service. It's peers,
+ * dependencies, and all other meta data related to the service.
+ *
+ */
+ public CalikoMeta() {
+
+ // add a cool description
+ addDescription("used as a general template");
+
+ // false will prevent it being seen in the ui
+ setAvailable(true);
+
+ // add dependencies if necessary
+ // for the solver
+ addDependency("au.edu.federation.caliko", "caliko", "1.3.8");
+
+ // for the ui
+ addDependency("au.edu.federation.caliko.visualisation", "caliko-visualisation", "1.3.8");
+ addDependency("au.edu.federation.caliko.demo", "caliko-demo", "1.3.8");
+
+ // add it to one or many categories
+ addCategory("ik", "inverse kinematics");
+
+ // add a sponsor to this service
+ // the person who will do maintenance
+ // setSponsor("GroG");
+
+ }
+
+}
diff --git a/src/main/java/org/myrobotlab/service/meta/ProgramABMeta.java b/src/main/java/org/myrobotlab/service/meta/ProgramABMeta.java
index 7430f362dd..94ce962ed2 100644
--- a/src/main/java/org/myrobotlab/service/meta/ProgramABMeta.java
+++ b/src/main/java/org/myrobotlab/service/meta/ProgramABMeta.java
@@ -32,7 +32,8 @@ public ProgramABMeta() {
// TODO: This is for CJK support in ProgramAB move this into the published
// POM for ProgramAB so they are pulled in transiently.
addDependency("org.apache.lucene", "lucene-analysis-common", "9.4.2");
- addDependency("org.apache.lucene", "lucene-analysis-kuromoji", "9.4.2");
+ addDependency("org.apache.lucene", "lucene-analysis-kuromoji", "9.4.2");
+ addDependency("org.openjdk.nashorn", "nashorn-core", "15.4");
addCategory("ai", "control");
}
diff --git a/src/main/java/org/myrobotlab/service/meta/SlackBotMeta.java b/src/main/java/org/myrobotlab/service/meta/SlackBotMeta.java
deleted file mode 100755
index 83240012c0..0000000000
--- a/src/main/java/org/myrobotlab/service/meta/SlackBotMeta.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.myrobotlab.service.meta;
-
-import org.myrobotlab.service.meta.abstracts.MetaData;
-
-public class SlackBotMeta extends MetaData {
- private static final long serialVersionUID = 1L;
-
- /**
- * SlackBot Metadata This uses the Slack official API.
- *
- */
- public SlackBotMeta() {
- super();
- addDescription("Slack Bot Proxy for chatbot backend.");
- addCategory("chatbot");
- addDependency("com.slack.api", "bolt", "1.24.0");
- addDependency("com.slack.api", "bolt-socket-mode", "1.24.0");
- addDependency("javax.websocket", "javax.websocket-api", "1.1");
- addDependency("org.glassfish.tyrus.bundles", "tyrus-standalone-client", "1.19");
- }
-
-}
diff --git a/src/main/resources/resource/Arduino/MrlComm/Adafruit_NeoPixel.cpp b/src/main/resources/resource/Arduino/MrlComm/Adafruit_NeoPixel.cpp
index fe4e89e8a2..f2881a5356 100644
--- a/src/main/resources/resource/Arduino/MrlComm/Adafruit_NeoPixel.cpp
+++ b/src/main/resources/resource/Arduino/MrlComm/Adafruit_NeoPixel.cpp
@@ -46,7 +46,7 @@
#include "Adafruit_NeoPixel.h"
#if defined(TARGET_LPC1768)
- #include
+#include
#endif
#if defined(NRF52) || defined(NRF52_SERIES)
@@ -59,9 +59,9 @@
#if defined(ARDUINO_ARCH_NRF52840)
#if defined __has_include
-# if __has_include ()
-# include
-# endif
+#if __has_include()
+#include
+#endif
#endif
#endif
@@ -77,11 +77,21 @@
pixel.
@return Adafruit_NeoPixel object. Call the begin() function before use.
*/
-Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) :
- begun(false), brightness(0), pixels(NULL), endTime(0) {
+Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, int16_t p, neoPixelType t)
+ : begun(false), brightness(0), pixels(NULL), endTime(0) {
updateType(t);
updateLength(n);
setPin(p);
+#if defined(ARDUINO_ARCH_RP2040)
+ // Find a free SM on one of the PIO's
+ sm = pio_claim_unused_sm(pio, false); // don't panic
+ // Try pio1 if SM not found
+ if (sm < 0) {
+ pio = pio1;
+ sm = pio_claim_unused_sm(pio, true); // panic if no SM is free
+ }
+ init = true;
+#endif
}
/*!
@@ -94,12 +104,13 @@ Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) :
'new' keyword with the first constructor syntax (length, pin,
type).
*/
-Adafruit_NeoPixel::Adafruit_NeoPixel() :
+Adafruit_NeoPixel::Adafruit_NeoPixel()
+ :
#if defined(NEO_KHZ400)
- is800KHz(true),
+ is800KHz(true),
#endif
- begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), pixels(NULL),
- rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) {
+ begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0),
+ pixels(NULL), rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) {
}
/*!
@@ -107,14 +118,15 @@ Adafruit_NeoPixel::Adafruit_NeoPixel() :
*/
Adafruit_NeoPixel::~Adafruit_NeoPixel() {
free(pixels);
- if(pin >= 0) pinMode(pin, INPUT);
+ if (pin >= 0)
+ pinMode(pin, INPUT);
}
/*!
@brief Configure NeoPixel pin for output.
*/
void Adafruit_NeoPixel::begin(void) {
- if(pin >= 0) {
+ if (pin >= 0) {
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
@@ -136,7 +148,7 @@ void Adafruit_NeoPixel::updateLength(uint16_t n) {
// Allocate new data -- note: ALL PIXELS ARE CLEARED
numBytes = n * ((wOffset == rOffset) ? 3 : 4);
- if((pixels = (uint8_t *)malloc(numBytes))) {
+ if ((pixels = (uint8_t *)malloc(numBytes))) {
memset(pixels, 0, numBytes);
numLEDs = n;
} else {
@@ -167,41 +179,71 @@ void Adafruit_NeoPixel::updateType(neoPixelType t) {
wOffset = (t >> 6) & 0b11; // See notes in header file
rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets
gOffset = (t >> 2) & 0b11;
- bOffset = t & 0b11;
+ bOffset = t & 0b11;
#if defined(NEO_KHZ400)
- is800KHz = (t < 256); // 400 KHz flag is 1<<8
+ is800KHz = (t < 256); // 400 KHz flag is 1<<8
#endif
// If bytes-per-pixel has changed (and pixel data was previously
// allocated), re-allocate to new size. Will clear any data.
- if(pixels) {
+ if (pixels) {
bool newThreeBytesPerPixel = (wOffset == rOffset);
- if(newThreeBytesPerPixel != oldThreeBytesPerPixel) updateLength(numLEDs);
+ if (newThreeBytesPerPixel != oldThreeBytesPerPixel)
+ updateLength(numLEDs);
}
}
+// RP2040 specific driver
#if defined(ARDUINO_ARCH_RP2040)
-extern "C" void rp2040Show(
- uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type);
+void Adafruit_NeoPixel::rp2040Init(uint8_t pin, bool is800KHz)
+{
+ uint offset = pio_add_program(pio, &ws2812_program);
+
+ if (is800KHz)
+ {
+ // 800kHz, 8 bit transfers
+ ws2812_program_init(pio, sm, offset, pin, 800000, 8);
+ }
+ else
+ {
+ // 400kHz, 8 bit transfers
+ ws2812_program_init(pio, sm, offset, pin, 400000, 8);
+ }
+}
+// Not a user API
+void Adafruit_NeoPixel::rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz)
+{
+ if (this->init)
+ {
+ // On first pass through initialise the PIO
+ rp2040Init(pin, is800KHz);
+ this->init = false;
+ }
+
+ while(numBytes--)
+ // Bits for transmission must be shifted to top 8 bits
+ pio_sm_put_blocking(pio, sm, ((uint32_t)*pixels++)<< 24);
+}
+
#endif
#if defined(ESP8266)
// ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution
-extern "C" void ICACHE_RAM_ATTR espShow(
- uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type);
+extern "C" IRAM_ATTR void espShow(uint16_t pin, uint8_t *pixels,
+ uint32_t numBytes, uint8_t type);
#elif defined(ESP32)
-extern "C" void espShow(
- uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type);
+extern "C" void espShow(uint16_t pin, uint8_t *pixels, uint32_t numBytes,
+ uint8_t type);
#endif // ESP8266
-#if defined(K210)
+#if defined(K210)
#define KENDRYTE_K210 1
#endif
#if defined(KENDRYTE_K210)
-extern "C" void k210Show(
- uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz);
-#endif //KENDRYTE_K210
+extern "C" void k210Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes,
+ boolean is800KHz);
+#endif // KENDRYTE_K210
/*!
@brief Transmit pixel data in RAM to NeoPixels.
@note On most architectures, interrupts are temporarily disabled in
@@ -215,7 +257,8 @@ extern "C" void k210Show(
*/
void Adafruit_NeoPixel::show(void) {
- if(!pixels) return;
+ if (!pixels)
+ return;
// Data latch = 300+ microsecond pause in the output stream. Rather than
// put a delay at the end of the function, the ending time is noted and
@@ -223,36 +266,36 @@ void Adafruit_NeoPixel::show(void) {
// subsequent round of data until the latch time has elapsed. This
// allows the mainline code to start generating the next frame of data
// rather than stalling for the latch.
- while(!canShow());
- // endTime is a private member (rather than global var) so that multiple
- // instances on different pins can be quickly issued in succession (each
- // instance doesn't delay the next).
-
- // In order to make this code runtime-configurable to work with any pin,
- // SBI/CBI instructions are eschewed in favor of full PORT writes via the
- // OUT or ST instructions. It relies on two facts: that peripheral
- // functions (such as PWM) take precedence on output pins, so our PORT-
- // wide writes won't interfere, and that interrupts are globally disabled
- // while data is being issued to the LEDs, so no other code will be
- // accessing the PORT. The code takes an initial 'snapshot' of the PORT
- // state, computes 'pin high' and 'pin low' values, and writes these back
- // to the PORT register as needed.
-
- // NRF52 may use PWM + DMA (if available), may not need to disable interrupt
-#if !( defined(NRF52) || defined(NRF52_SERIES) )
+ while (!canShow())
+ ;
+ // endTime is a private member (rather than global var) so that multiple
+ // instances on different pins can be quickly issued in succession (each
+ // instance doesn't delay the next).
+
+ // In order to make this code runtime-configurable to work with any pin,
+ // SBI/CBI instructions are eschewed in favor of full PORT writes via the
+ // OUT or ST instructions. It relies on two facts: that peripheral
+ // functions (such as PWM) take precedence on output pins, so our PORT-
+ // wide writes won't interfere, and that interrupts are globally disabled
+ // while data is being issued to the LEDs, so no other code will be
+ // accessing the PORT. The code takes an initial 'snapshot' of the PORT
+ // state, computes 'pin high' and 'pin low' values, and writes these back
+ // to the PORT register as needed.
+
+ // NRF52 may use PWM + DMA (if available), may not need to disable interrupt
+ // ESP32 may not disable interrupts because espShow() uses RMT which tries to acquire locks
+#if !(defined(NRF52) || defined(NRF52_SERIES) || defined(ESP32))
noInterrupts(); // Need 100% focus on instruction timing
#endif
#if defined(__AVR__)
-// AVR MCUs -- ATmega & ATtiny (no XMEGA) ---------------------------------
+ // AVR MCUs -- ATmega & ATtiny (no XMEGA) ---------------------------------
- volatile uint16_t
- i = numBytes; // Loop counter
- volatile uint8_t
- *ptr = pixels, // Pointer to next byte
- b = *ptr++, // Current byte value
- hi, // PORT w/output bit set high
- lo; // PORT w/output bit set low
+ volatile uint16_t i = numBytes; // Loop counter
+ volatile uint8_t *ptr = pixels, // Pointer to next byte
+ b = *ptr++, // Current byte value
+ hi, // PORT w/output bit set high
+ lo; // PORT w/output bit set low
// Hand-tuned assembly code issues data to the LED drivers at a specific
// rate. There's separate code for different CPU speeds (8, 12, 16 MHz)
@@ -272,10 +315,10 @@ void Adafruit_NeoPixel::show(void) {
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL)
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
- volatile uint8_t n1, n2 = 0; // First, next bits out
+ volatile uint8_t n1, n2 = 0; // First, next bits out
// Squeezing an 800 KHz stream out of an 8 MHz chip requires code
// specific to each PORT register.
@@ -286,14 +329,15 @@ void Adafruit_NeoPixel::show(void) {
// PORTD OUTPUT ----------------------------------------------------
#if defined(PORTD)
- #if defined(PORTB) || defined(PORTC) || defined(PORTF)
- if(port == &PORTD) {
- #endif // defined(PORTB/C/F)
+#if defined(PORTB) || defined(PORTC) || defined(PORTF)
+ if (port == &PORTD) {
+#endif // defined(PORTB/C/F)
- hi = PORTD | pinMask;
+ hi = PORTD | pinMask;
lo = PORTD & ~pinMask;
n1 = lo;
- if(b & 0x80) n1 = hi;
+ if (b & 0x80)
+ n1 = hi;
// Dirty trick: RJMPs proceeding to the next instruction are used
// to delay two clock cycles in one instruction word (rather than
@@ -302,360 +346,618 @@ void Adafruit_NeoPixel::show(void) {
// relative branch.
asm volatile(
- "headD:" "\n\t" // Clk Pseudocode
- // Bit 7:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
- "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
- "out %[port] , %[n1]" "\n\t" // 1 PORT = n1
- "rjmp .+0" "\n\t" // 2 nop nop
- "sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40)
- "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
- "rjmp .+0" "\n\t" // 2 nop nop
- // Bit 6:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
- "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
- "out %[port] , %[n2]" "\n\t" // 1 PORT = n2
- "rjmp .+0" "\n\t" // 2 nop nop
- "sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20)
- "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
- "rjmp .+0" "\n\t" // 2 nop nop
- // Bit 5:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
- "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
- "out %[port] , %[n1]" "\n\t" // 1 PORT = n1
- "rjmp .+0" "\n\t" // 2 nop nop
- "sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10)
- "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
- "rjmp .+0" "\n\t" // 2 nop nop
- // Bit 4:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
- "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
- "out %[port] , %[n2]" "\n\t" // 1 PORT = n2
- "rjmp .+0" "\n\t" // 2 nop nop
- "sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08)
- "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
- "rjmp .+0" "\n\t" // 2 nop nop
- // Bit 3:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
- "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
- "out %[port] , %[n1]" "\n\t" // 1 PORT = n1
- "rjmp .+0" "\n\t" // 2 nop nop
- "sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04)
- "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
- "rjmp .+0" "\n\t" // 2 nop nop
- // Bit 2:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
- "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
- "out %[port] , %[n2]" "\n\t" // 1 PORT = n2
- "rjmp .+0" "\n\t" // 2 nop nop
- "sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02)
- "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
- "rjmp .+0" "\n\t" // 2 nop nop
- // Bit 1:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
- "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
- "out %[port] , %[n1]" "\n\t" // 1 PORT = n1
- "rjmp .+0" "\n\t" // 2 nop nop
- "sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01)
- "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
- "sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet)
- // Bit 0:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
- "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
- "out %[port] , %[n2]" "\n\t" // 1 PORT = n2
- "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++
- "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80)
- "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
- "brne headD" "\n" // 2 while(i) (Z flag set above)
- : [byte] "+r" (b),
- [n1] "+r" (n1),
- [n2] "+r" (n2),
- [count] "+w" (i)
- : [port] "I" (_SFR_IO_ADDR(PORTD)),
- [ptr] "e" (ptr),
- [hi] "r" (hi),
- [lo] "r" (lo));
-
- #if defined(PORTB) || defined(PORTC) || defined(PORTF)
+ "headD:"
+ "\n\t" // Clk Pseudocode
+ // Bit 7:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi
+ "mov %[n2] , %[lo]"
+ "\n\t" // 1 n2 = lo
+ "out %[port] , %[n1]"
+ "\n\t" // 1 PORT = n1
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ "sbrc %[byte] , 6"
+ "\n\t" // 1-2 if(b & 0x40)
+ "mov %[n2] , %[hi]"
+ "\n\t" // 0-1 n2 = hi
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ // Bit 6:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi
+ "mov %[n1] , %[lo]"
+ "\n\t" // 1 n1 = lo
+ "out %[port] , %[n2]"
+ "\n\t" // 1 PORT = n2
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ "sbrc %[byte] , 5"
+ "\n\t" // 1-2 if(b & 0x20)
+ "mov %[n1] , %[hi]"
+ "\n\t" // 0-1 n1 = hi
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ // Bit 5:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi
+ "mov %[n2] , %[lo]"
+ "\n\t" // 1 n2 = lo
+ "out %[port] , %[n1]"
+ "\n\t" // 1 PORT = n1
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ "sbrc %[byte] , 4"
+ "\n\t" // 1-2 if(b & 0x10)
+ "mov %[n2] , %[hi]"
+ "\n\t" // 0-1 n2 = hi
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ // Bit 4:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi
+ "mov %[n1] , %[lo]"
+ "\n\t" // 1 n1 = lo
+ "out %[port] , %[n2]"
+ "\n\t" // 1 PORT = n2
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ "sbrc %[byte] , 3"
+ "\n\t" // 1-2 if(b & 0x08)
+ "mov %[n1] , %[hi]"
+ "\n\t" // 0-1 n1 = hi
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ // Bit 3:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi
+ "mov %[n2] , %[lo]"
+ "\n\t" // 1 n2 = lo
+ "out %[port] , %[n1]"
+ "\n\t" // 1 PORT = n1
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ "sbrc %[byte] , 2"
+ "\n\t" // 1-2 if(b & 0x04)
+ "mov %[n2] , %[hi]"
+ "\n\t" // 0-1 n2 = hi
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ // Bit 2:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi
+ "mov %[n1] , %[lo]"
+ "\n\t" // 1 n1 = lo
+ "out %[port] , %[n2]"
+ "\n\t" // 1 PORT = n2
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ "sbrc %[byte] , 1"
+ "\n\t" // 1-2 if(b & 0x02)
+ "mov %[n1] , %[hi]"
+ "\n\t" // 0-1 n1 = hi
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ // Bit 1:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi
+ "mov %[n2] , %[lo]"
+ "\n\t" // 1 n2 = lo
+ "out %[port] , %[n1]"
+ "\n\t" // 1 PORT = n1
+ "rjmp .+0"
+ "\n\t" // 2 nop nop
+ "sbrc %[byte] , 0"
+ "\n\t" // 1-2 if(b & 0x01)
+ "mov %[n2] , %[hi]"
+ "\n\t" // 0-1 n2 = hi
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo
+ "sbiw %[count], 1"
+ "\n\t" // 2 i-- (don't act on Z flag yet)
+ // Bit 0:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi
+ "mov %[n1] , %[lo]"
+ "\n\t" // 1 n1 = lo
+ "out %[port] , %[n2]"
+ "\n\t" // 1 PORT = n2
+ "ld %[byte] , %a[ptr]+"
+ "\n\t" // 2 b = *ptr++
+ "sbrc %[byte] , 7"
+ "\n\t" // 1-2 if(b & 0x80)
+ "mov %[n1] , %[hi]"
+ "\n\t" // 0-1 n1 = hi
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo
+ "brne headD"
+ "\n" // 2 while(i) (Z flag set above)
+ : [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i)
+ : [port] "I"(_SFR_IO_ADDR(PORTD)), [ptr] "e"(ptr), [hi] "r"(hi),
+ [lo] "r"(lo));
+
+#if defined(PORTB) || defined(PORTC) || defined(PORTF)
} else // other PORT(s)
- #endif // defined(PORTB/C/F)
+#endif // defined(PORTB/C/F)
#endif // defined(PORTD)
// PORTB OUTPUT ----------------------------------------------------
#if defined(PORTB)
- #if defined(PORTD) || defined(PORTC) || defined(PORTF)
- if(port == &PORTB) {
- #endif // defined(PORTD/C/F)
+#if defined(PORTD) || defined(PORTC) || defined(PORTF)
+ if (port == &PORTB) {
+#endif // defined(PORTD/C/F)
// Same as above, just switched to PORTB and stripped of comments.
- hi = PORTB | pinMask;
+ hi = PORTB | pinMask;
lo = PORTB & ~pinMask;
n1 = lo;
- if(b & 0x80) n1 = hi;
+ if (b & 0x80)
+ n1 = hi;
asm volatile(
- "headB:" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 6" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 5" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 4" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 3" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 2" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 1" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 0" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "sbiw %[count], 1" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "ld %[byte] , %a[ptr]+" "\n\t"
- "sbrc %[byte] , 7" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "brne headB" "\n"
- : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i)
- : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi),
- [lo] "r" (lo));
-
- #if defined(PORTD) || defined(PORTC) || defined(PORTF)
+ "headB:"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 6"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 5"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 4"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 3"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 2"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 1"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 0"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "sbiw %[count], 1"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "ld %[byte] , %a[ptr]+"
+ "\n\t"
+ "sbrc %[byte] , 7"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "brne headB"
+ "\n"
+ : [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i)
+ : [port] "I"(_SFR_IO_ADDR(PORTB)), [ptr] "e"(ptr), [hi] "r"(hi),
+ [lo] "r"(lo));
+
+#if defined(PORTD) || defined(PORTC) || defined(PORTF)
}
- #endif
- #if defined(PORTC) || defined(PORTF)
+#endif
+#if defined(PORTC) || defined(PORTF)
else
- #endif // defined(PORTC/F)
+#endif // defined(PORTC/F)
#endif // defined(PORTB)
// PORTC OUTPUT ----------------------------------------------------
#if defined(PORTC)
- #if defined(PORTD) || defined(PORTB) || defined(PORTF)
- if(port == &PORTC) {
- #endif // defined(PORTD/B/F)
+#if defined(PORTD) || defined(PORTB) || defined(PORTF)
+ if (port == &PORTC) {
+#endif // defined(PORTD/B/F)
// Same as above, just switched to PORTC and stripped of comments.
- hi = PORTC | pinMask;
+ hi = PORTC | pinMask;
lo = PORTC & ~pinMask;
n1 = lo;
- if(b & 0x80) n1 = hi;
+ if (b & 0x80)
+ n1 = hi;
asm volatile(
- "headC:" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 6" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 5" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 4" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 3" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 2" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 1" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 0" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "sbiw %[count], 1" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "ld %[byte] , %a[ptr]+" "\n\t"
- "sbrc %[byte] , 7" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "brne headC" "\n"
- : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i)
- : [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi),
- [lo] "r" (lo));
-
- #if defined(PORTD) || defined(PORTB) || defined(PORTF)
+ "headC:"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 6"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 5"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 4"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 3"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 2"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 1"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 0"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "sbiw %[count], 1"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "ld %[byte] , %a[ptr]+"
+ "\n\t"
+ "sbrc %[byte] , 7"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "brne headC"
+ "\n"
+ : [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i)
+ : [port] "I"(_SFR_IO_ADDR(PORTC)), [ptr] "e"(ptr), [hi] "r"(hi),
+ [lo] "r"(lo));
+
+#if defined(PORTD) || defined(PORTB) || defined(PORTF)
}
- #endif // defined(PORTD/B/F)
- #if defined(PORTF)
+#endif // defined(PORTD/B/F)
+#if defined(PORTF)
else
- #endif
+#endif
#endif // defined(PORTC)
// PORTF OUTPUT ----------------------------------------------------
#if defined(PORTF)
- #if defined(PORTD) || defined(PORTB) || defined(PORTC)
- if(port == &PORTF) {
- #endif // defined(PORTD/B/C)
+#if defined(PORTD) || defined(PORTB) || defined(PORTC)
+ if (port == &PORTF) {
+#endif // defined(PORTD/B/C)
- hi = PORTF | pinMask;
+ hi = PORTF | pinMask;
lo = PORTF & ~pinMask;
n1 = lo;
- if(b & 0x80) n1 = hi;
+ if (b & 0x80)
+ n1 = hi;
asm volatile(
- "headF:" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 6" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 5" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 4" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 3" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 2" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 1" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "rjmp .+0" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n2] , %[lo]" "\n\t"
- "out %[port] , %[n1]" "\n\t"
- "rjmp .+0" "\n\t"
- "sbrc %[byte] , 0" "\n\t"
- "mov %[n2] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "sbiw %[count], 1" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "mov %[n1] , %[lo]" "\n\t"
- "out %[port] , %[n2]" "\n\t"
- "ld %[byte] , %a[ptr]+" "\n\t"
- "sbrc %[byte] , 7" "\n\t"
- "mov %[n1] , %[hi]" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "brne headF" "\n"
- : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i)
- : [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi),
- [lo] "r" (lo));
-
- #if defined(PORTD) || defined(PORTB) || defined(PORTC)
+ "headF:"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 6"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 5"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 4"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 3"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 2"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 1"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n2] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n1]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "sbrc %[byte] , 0"
+ "\n\t"
+ "mov %[n2] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "sbiw %[count], 1"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "mov %[n1] , %[lo]"
+ "\n\t"
+ "out %[port] , %[n2]"
+ "\n\t"
+ "ld %[byte] , %a[ptr]+"
+ "\n\t"
+ "sbrc %[byte] , 7"
+ "\n\t"
+ "mov %[n1] , %[hi]"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "brne headF"
+ "\n"
+ : [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i)
+ : [port] "I"(_SFR_IO_ADDR(PORTF)), [ptr] "e"(ptr), [hi] "r"(hi),
+ [lo] "r"(lo));
+
+#if defined(PORTD) || defined(PORTB) || defined(PORTC)
}
- #endif // defined(PORTD/B/C)
+#endif // defined(PORTD/B/C)
#endif // defined(PORTF)
#if defined(NEO_KHZ400)
@@ -673,41 +975,56 @@ void Adafruit_NeoPixel::show(void) {
volatile uint8_t next, bit;
- hi = *port | pinMask;
- lo = *port & ~pinMask;
+ hi = *port | pinMask;
+ lo = *port & ~pinMask;
next = lo;
- bit = 8;
-
- asm volatile(
- "head20:" "\n\t" // Clk Pseudocode (T = 0)
- "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
- "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
- "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
- "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6)
- "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7)
- "dec %[bit]" "\n\t" // 1 bit-- (T = 8)
- "breq nextbyte20" "\n\t" // 1-2 if(bit == 0)
- "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
- "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 14)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 16)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 18)
- "rjmp head20" "\n\t" // 2 -> head20 (next bit out)
- "nextbyte20:" "\n\t" // (T = 10)
- "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12)
- "nop" "\n\t" // 1 nop (T = 13)
- "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14)
- "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16)
- "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
- "brne head20" "\n" // 2 if(i != 0) -> (next byte)
- : [port] "+e" (port),
- [byte] "+r" (b),
- [bit] "+r" (bit),
- [next] "+r" (next),
- [count] "+w" (i)
- : [hi] "r" (hi),
- [lo] "r" (lo),
- [ptr] "e" (ptr));
+ bit = 8;
+
+ asm volatile("head20:"
+ "\n\t" // Clk Pseudocode (T = 0)
+ "st %a[port], %[hi]"
+ "\n\t" // 2 PORT = hi (T = 2)
+ "sbrc %[byte] , 7"
+ "\n\t" // 1-2 if(b & 128)
+ "mov %[next], %[hi]"
+ "\n\t" // 0-1 next = hi (T = 4)
+ "st %a[port], %[next]"
+ "\n\t" // 2 PORT = next (T = 6)
+ "mov %[next] , %[lo]"
+ "\n\t" // 1 next = lo (T = 7)
+ "dec %[bit]"
+ "\n\t" // 1 bit-- (T = 8)
+ "breq nextbyte20"
+ "\n\t" // 1-2 if(bit == 0)
+ "rol %[byte]"
+ "\n\t" // 1 b <<= 1 (T = 10)
+ "st %a[port], %[lo]"
+ "\n\t" // 2 PORT = lo (T = 12)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 14)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 16)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 18)
+ "rjmp head20"
+ "\n\t" // 2 -> head20 (next bit out)
+ "nextbyte20:"
+ "\n\t" // (T = 10)
+ "st %a[port], %[lo]"
+ "\n\t" // 2 PORT = lo (T = 12)
+ "nop"
+ "\n\t" // 1 nop (T = 13)
+ "ldi %[bit] , 8"
+ "\n\t" // 1 bit = 8 (T = 14)
+ "ld %[byte] , %a[ptr]+"
+ "\n\t" // 2 b = *ptr++ (T = 16)
+ "sbiw %[count], 1"
+ "\n\t" // 2 i-- (T = 18)
+ "brne head20"
+ "\n" // 2 if(i != 0) -> (next byte)
+ : [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit),
+ [next] "+r"(next), [count] "+w"(i)
+ : [hi] "r"(hi), [lo] "r"(lo), [ptr] "e"(ptr));
}
#endif // NEO_KHZ400
@@ -715,7 +1032,7 @@ void Adafruit_NeoPixel::show(void) {
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL)
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
// In the 12 MHz case, an optimized 800 KHz datastream (no dead time
@@ -730,253 +1047,397 @@ void Adafruit_NeoPixel::show(void) {
// PORTD OUTPUT ----------------------------------------------------
#if defined(PORTD)
- #if defined(PORTB) || defined(PORTC) || defined(PORTF)
- if(port == &PORTD) {
- #endif // defined(PORTB/C/F)
+#if defined(PORTB) || defined(PORTC) || defined(PORTF)
+ if (port == &PORTD) {
+#endif // defined(PORTB/C/F)
- hi = PORTD | pinMask;
- lo = PORTD & ~pinMask;
+ hi = PORTD | pinMask;
+ lo = PORTD & ~pinMask;
next = lo;
- if(b & 0x80) next = hi;
+ if (b & 0x80)
+ next = hi;
// Don't "optimize" the OUT calls into the bitTime subroutine;
// we're exploiting the RCALL and RET as 3- and 4-cycle NOPs!
- asm volatile(
- "headD:" "\n\t" // (T = 0)
- "out %[port], %[hi]" "\n\t" // (T = 1)
- "rcall bitTimeD" "\n\t" // Bit 7 (T = 15)
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeD" "\n\t" // Bit 6
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeD" "\n\t" // Bit 5
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeD" "\n\t" // Bit 4
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeD" "\n\t" // Bit 3
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeD" "\n\t" // Bit 2
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeD" "\n\t" // Bit 1
- // Bit 0:
- "out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 3)
- "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5)
- "out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6)
- "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7)
- "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8)
- "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9)
- "nop" "\n\t" // 1 (T = 10)
- "out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11)
- "sbiw %[count], 1" "\n\t" // 2 i-- (T = 13)
- "brne headD" "\n\t" // 2 if(i != 0) -> (next byte)
- "rjmp doneD" "\n\t"
- "bitTimeD:" "\n\t" // nop nop nop (T = 4)
- "out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5)
- "mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6)
- "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7)
- "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8)
- "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9)
- "nop" "\n\t" // 1 (T = 10)
- "out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11)
- "ret" "\n\t" // 4 nop nop nop nop (T = 15)
- "doneD:" "\n"
- : [byte] "+r" (b),
- [next] "+r" (next),
- [count] "+w" (i)
- : [port] "I" (_SFR_IO_ADDR(PORTD)),
- [ptr] "e" (ptr),
- [hi] "r" (hi),
- [lo] "r" (lo));
-
- #if defined(PORTB) || defined(PORTC) || defined(PORTF)
+ asm volatile("headD:"
+ "\n\t" // (T = 0)
+ "out %[port], %[hi]"
+ "\n\t" // (T = 1)
+ "rcall bitTimeD"
+ "\n\t" // Bit 7 (T = 15)
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeD"
+ "\n\t" // Bit 6
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeD"
+ "\n\t" // Bit 5
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeD"
+ "\n\t" // Bit 4
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeD"
+ "\n\t" // Bit 3
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeD"
+ "\n\t" // Bit 2
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeD"
+ "\n\t" // Bit 1
+ // Bit 0:
+ "out %[port] , %[hi]"
+ "\n\t" // 1 PORT = hi (T = 1)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 3)
+ "ld %[byte] , %a[ptr]+"
+ "\n\t" // 2 b = *ptr++ (T = 5)
+ "out %[port] , %[next]"
+ "\n\t" // 1 PORT = next (T = 6)
+ "mov %[next] , %[lo]"
+ "\n\t" // 1 next = lo (T = 7)
+ "sbrc %[byte] , 7"
+ "\n\t" // 1-2 if(b & 0x80) (T = 8)
+ "mov %[next] , %[hi]"
+ "\n\t" // 0-1 next = hi (T = 9)
+ "nop"
+ "\n\t" // 1 (T = 10)
+ "out %[port] , %[lo]"
+ "\n\t" // 1 PORT = lo (T = 11)
+ "sbiw %[count], 1"
+ "\n\t" // 2 i-- (T = 13)
+ "brne headD"
+ "\n\t" // 2 if(i != 0) -> (next byte)
+ "rjmp doneD"
+ "\n\t"
+ "bitTimeD:"
+ "\n\t" // nop nop nop (T = 4)
+ "out %[port], %[next]"
+ "\n\t" // 1 PORT = next (T = 5)
+ "mov %[next], %[lo]"
+ "\n\t" // 1 next = lo (T = 6)
+ "rol %[byte]"
+ "\n\t" // 1 b <<= 1 (T = 7)
+ "sbrc %[byte], 7"
+ "\n\t" // 1-2 if(b & 0x80) (T = 8)
+ "mov %[next], %[hi]"
+ "\n\t" // 0-1 next = hi (T = 9)
+ "nop"
+ "\n\t" // 1 (T = 10)
+ "out %[port], %[lo]"
+ "\n\t" // 1 PORT = lo (T = 11)
+ "ret"
+ "\n\t" // 4 nop nop nop nop (T = 15)
+ "doneD:"
+ "\n"
+ : [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i)
+ : [port] "I"(_SFR_IO_ADDR(PORTD)), [ptr] "e"(ptr),
+ [hi] "r"(hi), [lo] "r"(lo));
+
+#if defined(PORTB) || defined(PORTC) || defined(PORTF)
} else // other PORT(s)
- #endif // defined(PORTB/C/F)
+#endif // defined(PORTB/C/F)
#endif // defined(PORTD)
// PORTB OUTPUT ----------------------------------------------------
#if defined(PORTB)
- #if defined(PORTD) || defined(PORTC) || defined(PORTF)
- if(port == &PORTB) {
- #endif // defined(PORTD/C/F)
+#if defined(PORTD) || defined(PORTC) || defined(PORTF)
+ if (port == &PORTB) {
+#endif // defined(PORTD/C/F)
- hi = PORTB | pinMask;
- lo = PORTB & ~pinMask;
+ hi = PORTB | pinMask;
+ lo = PORTB & ~pinMask;
next = lo;
- if(b & 0x80) next = hi;
+ if (b & 0x80)
+ next = hi;
// Same as above, just set for PORTB & stripped of comments
- asm volatile(
- "headB:" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeB" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeB" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeB" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeB" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeB" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeB" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeB" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "rjmp .+0" "\n\t"
- "ld %[byte] , %a[ptr]+" "\n\t"
- "out %[port] , %[next]" "\n\t"
- "mov %[next] , %[lo]" "\n\t"
- "sbrc %[byte] , 7" "\n\t"
- "mov %[next] , %[hi]" "\n\t"
- "nop" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "sbiw %[count], 1" "\n\t"
- "brne headB" "\n\t"
- "rjmp doneB" "\n\t"
- "bitTimeB:" "\n\t"
- "out %[port], %[next]" "\n\t"
- "mov %[next], %[lo]" "\n\t"
- "rol %[byte]" "\n\t"
- "sbrc %[byte], 7" "\n\t"
- "mov %[next], %[hi]" "\n\t"
- "nop" "\n\t"
- "out %[port], %[lo]" "\n\t"
- "ret" "\n\t"
- "doneB:" "\n"
- : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i)
- : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi),
- [lo] "r" (lo));
-
- #if defined(PORTD) || defined(PORTC) || defined(PORTF)
+ asm volatile("headB:"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeB"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeB"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeB"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeB"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeB"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeB"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeB"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "ld %[byte] , %a[ptr]+"
+ "\n\t"
+ "out %[port] , %[next]"
+ "\n\t"
+ "mov %[next] , %[lo]"
+ "\n\t"
+ "sbrc %[byte] , 7"
+ "\n\t"
+ "mov %[next] , %[hi]"
+ "\n\t"
+ "nop"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "sbiw %[count], 1"
+ "\n\t"
+ "brne headB"
+ "\n\t"
+ "rjmp doneB"
+ "\n\t"
+ "bitTimeB:"
+ "\n\t"
+ "out %[port], %[next]"
+ "\n\t"
+ "mov %[next], %[lo]"
+ "\n\t"
+ "rol %[byte]"
+ "\n\t"
+ "sbrc %[byte], 7"
+ "\n\t"
+ "mov %[next], %[hi]"
+ "\n\t"
+ "nop"
+ "\n\t"
+ "out %[port], %[lo]"
+ "\n\t"
+ "ret"
+ "\n\t"
+ "doneB:"
+ "\n"
+ : [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i)
+ : [port] "I"(_SFR_IO_ADDR(PORTB)), [ptr] "e"(ptr),
+ [hi] "r"(hi), [lo] "r"(lo));
+
+#if defined(PORTD) || defined(PORTC) || defined(PORTF)
}
- #endif
- #if defined(PORTC) || defined(PORTF)
+#endif
+#if defined(PORTC) || defined(PORTF)
else
- #endif // defined(PORTC/F)
+#endif // defined(PORTC/F)
#endif // defined(PORTB)
// PORTC OUTPUT ----------------------------------------------------
#if defined(PORTC)
- #if defined(PORTD) || defined(PORTB) || defined(PORTF)
- if(port == &PORTC) {
- #endif // defined(PORTD/B/F)
+#if defined(PORTD) || defined(PORTB) || defined(PORTF)
+ if (port == &PORTC) {
+#endif // defined(PORTD/B/F)
- hi = PORTC | pinMask;
- lo = PORTC & ~pinMask;
+ hi = PORTC | pinMask;
+ lo = PORTC & ~pinMask;
next = lo;
- if(b & 0x80) next = hi;
+ if (b & 0x80)
+ next = hi;
// Same as above, just set for PORTC & stripped of comments
- asm volatile(
- "headC:" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "rjmp .+0" "\n\t"
- "ld %[byte] , %a[ptr]+" "\n\t"
- "out %[port] , %[next]" "\n\t"
- "mov %[next] , %[lo]" "\n\t"
- "sbrc %[byte] , 7" "\n\t"
- "mov %[next] , %[hi]" "\n\t"
- "nop" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "sbiw %[count], 1" "\n\t"
- "brne headC" "\n\t"
- "rjmp doneC" "\n\t"
- "bitTimeC:" "\n\t"
- "out %[port], %[next]" "\n\t"
- "mov %[next], %[lo]" "\n\t"
- "rol %[byte]" "\n\t"
- "sbrc %[byte], 7" "\n\t"
- "mov %[next], %[hi]" "\n\t"
- "nop" "\n\t"
- "out %[port], %[lo]" "\n\t"
- "ret" "\n\t"
- "doneC:" "\n"
- : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i)
- : [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi),
- [lo] "r" (lo));
-
- #if defined(PORTD) || defined(PORTB) || defined(PORTF)
+ asm volatile("headC:"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "ld %[byte] , %a[ptr]+"
+ "\n\t"
+ "out %[port] , %[next]"
+ "\n\t"
+ "mov %[next] , %[lo]"
+ "\n\t"
+ "sbrc %[byte] , 7"
+ "\n\t"
+ "mov %[next] , %[hi]"
+ "\n\t"
+ "nop"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "sbiw %[count], 1"
+ "\n\t"
+ "brne headC"
+ "\n\t"
+ "rjmp doneC"
+ "\n\t"
+ "bitTimeC:"
+ "\n\t"
+ "out %[port], %[next]"
+ "\n\t"
+ "mov %[next], %[lo]"
+ "\n\t"
+ "rol %[byte]"
+ "\n\t"
+ "sbrc %[byte], 7"
+ "\n\t"
+ "mov %[next], %[hi]"
+ "\n\t"
+ "nop"
+ "\n\t"
+ "out %[port], %[lo]"
+ "\n\t"
+ "ret"
+ "\n\t"
+ "doneC:"
+ "\n"
+ : [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i)
+ : [port] "I"(_SFR_IO_ADDR(PORTC)), [ptr] "e"(ptr),
+ [hi] "r"(hi), [lo] "r"(lo));
+
+#if defined(PORTD) || defined(PORTB) || defined(PORTF)
}
- #endif // defined(PORTD/B/F)
- #if defined(PORTF)
+#endif // defined(PORTD/B/F)
+#if defined(PORTF)
else
- #endif
+#endif
#endif // defined(PORTC)
// PORTF OUTPUT ----------------------------------------------------
#if defined(PORTF)
- #if defined(PORTD) || defined(PORTB) || defined(PORTC)
- if(port == &PORTF) {
- #endif // defined(PORTD/B/C)
+#if defined(PORTD) || defined(PORTB) || defined(PORTC)
+ if (port == &PORTF) {
+#endif // defined(PORTD/B/C)
- hi = PORTF | pinMask;
- lo = PORTF & ~pinMask;
+ hi = PORTF | pinMask;
+ lo = PORTF & ~pinMask;
next = lo;
- if(b & 0x80) next = hi;
+ if (b & 0x80)
+ next = hi;
// Same as above, just set for PORTF & stripped of comments
- asm volatile(
- "headF:" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port], %[hi]" "\n\t"
- "rcall bitTimeC" "\n\t"
- "out %[port] , %[hi]" "\n\t"
- "rjmp .+0" "\n\t"
- "ld %[byte] , %a[ptr]+" "\n\t"
- "out %[port] , %[next]" "\n\t"
- "mov %[next] , %[lo]" "\n\t"
- "sbrc %[byte] , 7" "\n\t"
- "mov %[next] , %[hi]" "\n\t"
- "nop" "\n\t"
- "out %[port] , %[lo]" "\n\t"
- "sbiw %[count], 1" "\n\t"
- "brne headF" "\n\t"
- "rjmp doneC" "\n\t"
- "bitTimeC:" "\n\t"
- "out %[port], %[next]" "\n\t"
- "mov %[next], %[lo]" "\n\t"
- "rol %[byte]" "\n\t"
- "sbrc %[byte], 7" "\n\t"
- "mov %[next], %[hi]" "\n\t"
- "nop" "\n\t"
- "out %[port], %[lo]" "\n\t"
- "ret" "\n\t"
- "doneC:" "\n"
- : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i)
- : [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi),
- [lo] "r" (lo));
-
- #if defined(PORTD) || defined(PORTB) || defined(PORTC)
+ asm volatile("headF:"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port], %[hi]"
+ "\n\t"
+ "rcall bitTimeC"
+ "\n\t"
+ "out %[port] , %[hi]"
+ "\n\t"
+ "rjmp .+0"
+ "\n\t"
+ "ld %[byte] , %a[ptr]+"
+ "\n\t"
+ "out %[port] , %[next]"
+ "\n\t"
+ "mov %[next] , %[lo]"
+ "\n\t"
+ "sbrc %[byte] , 7"
+ "\n\t"
+ "mov %[next] , %[hi]"
+ "\n\t"
+ "nop"
+ "\n\t"
+ "out %[port] , %[lo]"
+ "\n\t"
+ "sbiw %[count], 1"
+ "\n\t"
+ "brne headF"
+ "\n\t"
+ "rjmp doneC"
+ "\n\t"
+ "bitTimeC:"
+ "\n\t"
+ "out %[port], %[next]"
+ "\n\t"
+ "mov %[next], %[lo]"
+ "\n\t"
+ "rol %[byte]"
+ "\n\t"
+ "sbrc %[byte], 7"
+ "\n\t"
+ "mov %[next], %[hi]"
+ "\n\t"
+ "nop"
+ "\n\t"
+ "out %[port], %[lo]"
+ "\n\t"
+ "ret"
+ "\n\t"
+ "doneC:"
+ "\n"
+ : [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i)
+ : [port] "I"(_SFR_IO_ADDR(PORTF)), [ptr] "e"(ptr),
+ [hi] "r"(hi), [lo] "r"(lo));
+
+#if defined(PORTD) || defined(PORTB) || defined(PORTC)
}
- #endif // defined(PORTD/B/C)
+#endif // defined(PORTD/B/C)
#endif // defined(PORTF)
#if defined(NEO_KHZ400)
@@ -987,45 +1448,64 @@ void Adafruit_NeoPixel::show(void) {
volatile uint8_t next, bit;
- hi = *port | pinMask;
- lo = *port & ~pinMask;
+ hi = *port | pinMask;
+ lo = *port & ~pinMask;
next = lo;
- bit = 8;
-
- asm volatile(
- "head30:" "\n\t" // Clk Pseudocode (T = 0)
- "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
- "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
- "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 6)
- "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 10)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 12)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 14)
- "nop" "\n\t" // 1 nop (T = 15)
- "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 19)
- "dec %[bit]" "\n\t" // 1 bit-- (T = 20)
- "breq nextbyte30" "\n\t" // 1-2 if(bit == 0)
- "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 24)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 26)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 28)
- "rjmp head30" "\n\t" // 2 -> head30 (next bit out)
- "nextbyte30:" "\n\t" // (T = 22)
- "nop" "\n\t" // 1 nop (T = 23)
- "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24)
- "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26)
- "sbiw %[count], 1" "\n\t" // 2 i-- (T = 28)
- "brne head30" "\n" // 1-2 if(i != 0) -> (next byte)
- : [port] "+e" (port),
- [byte] "+r" (b),
- [bit] "+r" (bit),
- [next] "+r" (next),
- [count] "+w" (i)
- : [hi] "r" (hi),
- [lo] "r" (lo),
- [ptr] "e" (ptr));
+ bit = 8;
+
+ asm volatile("head30:"
+ "\n\t" // Clk Pseudocode (T = 0)
+ "st %a[port], %[hi]"
+ "\n\t" // 2 PORT = hi (T = 2)
+ "sbrc %[byte] , 7"
+ "\n\t" // 1-2 if(b & 128)
+ "mov %[next], %[hi]"
+ "\n\t" // 0-1 next = hi (T = 4)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 6)
+ "st %a[port], %[next]"
+ "\n\t" // 2 PORT = next (T = 8)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 10)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 12)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 14)
+ "nop"
+ "\n\t" // 1 nop (T = 15)
+ "st %a[port], %[lo]"
+ "\n\t" // 2 PORT = lo (T = 17)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 19)
+ "dec %[bit]"
+ "\n\t" // 1 bit-- (T = 20)
+ "breq nextbyte30"
+ "\n\t" // 1-2 if(bit == 0)
+ "rol %[byte]"
+ "\n\t" // 1 b <<= 1 (T = 22)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 24)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 26)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 28)
+ "rjmp head30"
+ "\n\t" // 2 -> head30 (next bit out)
+ "nextbyte30:"
+ "\n\t" // (T = 22)
+ "nop"
+ "\n\t" // 1 nop (T = 23)
+ "ldi %[bit] , 8"
+ "\n\t" // 1 bit = 8 (T = 24)
+ "ld %[byte] , %a[ptr]+"
+ "\n\t" // 2 b = *ptr++ (T = 26)
+ "sbiw %[count], 1"
+ "\n\t" // 2 i-- (T = 28)
+ "brne head30"
+ "\n" // 1-2 if(i != 0) -> (next byte)
+ : [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit),
+ [next] "+r"(next), [count] "+w"(i)
+ : [hi] "r"(hi), [lo] "r"(lo), [ptr] "e"(ptr));
}
#endif // NEO_KHZ400
@@ -1033,7 +1513,7 @@ void Adafruit_NeoPixel::show(void) {
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L)
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
// WS2811 and WS2812 have different hi/lo duty cycles; this is
@@ -1044,42 +1524,58 @@ void Adafruit_NeoPixel::show(void) {
volatile uint8_t next, bit;
- hi = *port | pinMask;
- lo = *port & ~pinMask;
+ hi = *port | pinMask;
+ lo = *port & ~pinMask;
next = lo;
- bit = 8;
-
- asm volatile(
- "head20:" "\n\t" // Clk Pseudocode (T = 0)
- "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
- "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128)
- "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
- "dec %[bit]" "\n\t" // 1 bit-- (T = 5)
- "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7)
- "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8)
- "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above)
- "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 12)
- "nop" "\n\t" // 1 nop (T = 13)
- "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
- "nop" "\n\t" // 1 nop (T = 16)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 18)
- "rjmp head20" "\n\t" // 2 -> head20 (next bit out)
- "nextbyte20:" "\n\t" // (T = 10)
- "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11)
- "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13)
- "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
- "nop" "\n\t" // 1 nop (T = 16)
- "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
- "brne head20" "\n" // 2 if(i != 0) -> (next byte)
- : [port] "+e" (port),
- [byte] "+r" (b),
- [bit] "+r" (bit),
- [next] "+r" (next),
- [count] "+w" (i)
- : [ptr] "e" (ptr),
- [hi] "r" (hi),
- [lo] "r" (lo));
+ bit = 8;
+
+ asm volatile("head20:"
+ "\n\t" // Clk Pseudocode (T = 0)
+ "st %a[port], %[hi]"
+ "\n\t" // 2 PORT = hi (T = 2)
+ "sbrc %[byte], 7"
+ "\n\t" // 1-2 if(b & 128)
+ "mov %[next], %[hi]"
+ "\n\t" // 0-1 next = hi (T = 4)
+ "dec %[bit]"
+ "\n\t" // 1 bit-- (T = 5)
+ "st %a[port], %[next]"
+ "\n\t" // 2 PORT = next (T = 7)
+ "mov %[next] , %[lo]"
+ "\n\t" // 1 next = lo (T = 8)
+ "breq nextbyte20"
+ "\n\t" // 1-2 if(bit == 0) (from dec above)
+ "rol %[byte]"
+ "\n\t" // 1 b <<= 1 (T = 10)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 12)
+ "nop"
+ "\n\t" // 1 nop (T = 13)
+ "st %a[port], %[lo]"
+ "\n\t" // 2 PORT = lo (T = 15)
+ "nop"
+ "\n\t" // 1 nop (T = 16)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 18)
+ "rjmp head20"
+ "\n\t" // 2 -> head20 (next bit out)
+ "nextbyte20:"
+ "\n\t" // (T = 10)
+ "ldi %[bit] , 8"
+ "\n\t" // 1 bit = 8 (T = 11)
+ "ld %[byte] , %a[ptr]+"
+ "\n\t" // 2 b = *ptr++ (T = 13)
+ "st %a[port], %[lo]"
+ "\n\t" // 2 PORT = lo (T = 15)
+ "nop"
+ "\n\t" // 1 nop (T = 16)
+ "sbiw %[count], 1"
+ "\n\t" // 2 i-- (T = 18)
+ "brne head20"
+ "\n" // 2 if(i != 0) -> (next byte)
+ : [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit),
+ [next] "+r"(next), [count] "+w"(i)
+ : [ptr] "e"(ptr), [hi] "r"(hi), [lo] "r"(lo));
#if defined(NEO_KHZ400)
} else { // 400 KHz
@@ -1091,273 +1587,368 @@ void Adafruit_NeoPixel::show(void) {
volatile uint8_t next, bit;
- hi = *port | pinMask;
- lo = *port & ~pinMask;
+ hi = *port | pinMask;
+ lo = *port & ~pinMask;
next = lo;
- bit = 8;
-
- asm volatile(
- "head40:" "\n\t" // Clk Pseudocode (T = 0)
- "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
- "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
- "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 6)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 8)
- "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 12)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 14)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 16)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 18)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 20)
- "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22)
- "nop" "\n\t" // 1 nop (T = 23)
- "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24)
- "dec %[bit]" "\n\t" // 1 bit-- (T = 25)
- "breq nextbyte40" "\n\t" // 1-2 if(bit == 0)
- "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27)
- "nop" "\n\t" // 1 nop (T = 28)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 30)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 32)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 34)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 36)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 38)
- "rjmp head40" "\n\t" // 2 -> head40 (next bit out)
- "nextbyte40:" "\n\t" // (T = 27)
- "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28)
- "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 32)
- "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34)
- "rjmp .+0" "\n\t" // 2 nop nop (T = 36)
- "sbiw %[count], 1" "\n\t" // 2 i-- (T = 38)
- "brne head40" "\n" // 1-2 if(i != 0) -> (next byte)
- : [port] "+e" (port),
- [byte] "+r" (b),
- [bit] "+r" (bit),
- [next] "+r" (next),
- [count] "+w" (i)
- : [ptr] "e" (ptr),
- [hi] "r" (hi),
- [lo] "r" (lo));
+ bit = 8;
+
+ asm volatile("head40:"
+ "\n\t" // Clk Pseudocode (T = 0)
+ "st %a[port], %[hi]"
+ "\n\t" // 2 PORT = hi (T = 2)
+ "sbrc %[byte] , 7"
+ "\n\t" // 1-2 if(b & 128)
+ "mov %[next] , %[hi]"
+ "\n\t" // 0-1 next = hi (T = 4)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 6)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 8)
+ "st %a[port], %[next]"
+ "\n\t" // 2 PORT = next (T = 10)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 12)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 14)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 16)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 18)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 20)
+ "st %a[port], %[lo]"
+ "\n\t" // 2 PORT = lo (T = 22)
+ "nop"
+ "\n\t" // 1 nop (T = 23)
+ "mov %[next] , %[lo]"
+ "\n\t" // 1 next = lo (T = 24)
+ "dec %[bit]"
+ "\n\t" // 1 bit-- (T = 25)
+ "breq nextbyte40"
+ "\n\t" // 1-2 if(bit == 0)
+ "rol %[byte]"
+ "\n\t" // 1 b <<= 1 (T = 27)
+ "nop"
+ "\n\t" // 1 nop (T = 28)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 30)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 32)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 34)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 36)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 38)
+ "rjmp head40"
+ "\n\t" // 2 -> head40 (next bit out)
+ "nextbyte40:"
+ "\n\t" // (T = 27)
+ "ldi %[bit] , 8"
+ "\n\t" // 1 bit = 8 (T = 28)
+ "ld %[byte] , %a[ptr]+"
+ "\n\t" // 2 b = *ptr++ (T = 30)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 32)
+ "st %a[port], %[lo]"
+ "\n\t" // 2 PORT = lo (T = 34)
+ "rjmp .+0"
+ "\n\t" // 2 nop nop (T = 36)
+ "sbiw %[count], 1"
+ "\n\t" // 2 i-- (T = 38)
+ "brne head40"
+ "\n" // 1-2 if(i != 0) -> (next byte)
+ : [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit),
+ [next] "+r"(next), [count] "+w"(i)
+ : [ptr] "e"(ptr), [hi] "r"(hi), [lo] "r"(lo));
}
#endif // NEO_KHZ400
#else
- #error "CPU SPEED NOT SUPPORTED"
+#error "CPU SPEED NOT SUPPORTED"
#endif // end F_CPU ifdefs on __AVR__
-// END AVR ----------------------------------------------------------------
-
+ // END AVR ----------------------------------------------------------------
#elif defined(__arm__)
-// ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due, RP2040 -------------------
+ // ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due, RP2040 -------------------
#if defined(ARDUINO_ARCH_RP2040)
// Use PIO
rp2040Show(pin, pixels, numBytes, is800KHz);
-#elif defined(TEENSYDUINO) && defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6
-#define CYCLES_800_T0H (F_CPU / 4000000)
-#define CYCLES_800_T1H (F_CPU / 1250000)
-#define CYCLES_800 (F_CPU / 800000)
-#define CYCLES_400_T0H (F_CPU / 2000000)
-#define CYCLES_400_T1H (F_CPU / 833333)
-#define CYCLES_400 (F_CPU / 400000)
-
- uint8_t *p = pixels,
- *end = p + numBytes, pix, mask;
- volatile uint8_t *set = portSetRegister(pin),
- *clr = portClearRegister(pin);
- uint32_t cyc;
-
- ARM_DEMCR |= ARM_DEMCR_TRCENA;
+#elif defined(TEENSYDUINO) && \
+ defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6
+#define CYCLES_800_T0H (F_CPU / 4000000)
+#define CYCLES_800_T1H (F_CPU / 1250000)
+#define CYCLES_800 (F_CPU / 800000)
+#define CYCLES_400_T0H (F_CPU / 2000000)
+#define CYCLES_400_T1H (F_CPU / 833333)
+#define CYCLES_400 (F_CPU / 400000)
+
+ uint8_t *p = pixels, *end = p + numBytes, pix, mask;
+ volatile uint8_t *set = portSetRegister(pin), *clr = portClearRegister(pin);
+ uint32_t cyc;
+
+ ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
cyc = ARM_DWT_CYCCNT + CYCLES_800;
- while(p < end) {
+ while (p < end) {
pix = *p++;
- for(mask = 0x80; mask; mask >>= 1) {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_800);
- cyc = ARM_DWT_CYCCNT;
+ for (mask = 0x80; mask; mask >>= 1) {
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
+ ;
+ cyc = ARM_DWT_CYCCNT;
*set = 1;
- if(pix & mask) {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H);
+ if (pix & mask) {
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H)
+ ;
} else {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H);
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H)
+ ;
}
*clr = 1;
}
}
- while(ARM_DWT_CYCCNT - cyc < CYCLES_800);
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
+ ;
#if defined(NEO_KHZ400)
} else { // 400 kHz bitstream
cyc = ARM_DWT_CYCCNT + CYCLES_400;
- while(p < end) {
+ while (p < end) {
pix = *p++;
- for(mask = 0x80; mask; mask >>= 1) {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_400);
- cyc = ARM_DWT_CYCCNT;
+ for (mask = 0x80; mask; mask >>= 1) {
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_400)
+ ;
+ cyc = ARM_DWT_CYCCNT;
*set = 1;
- if(pix & mask) {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H);
+ if (pix & mask) {
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H)
+ ;
} else {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H);
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H)
+ ;
}
*clr = 1;
}
}
- while(ARM_DWT_CYCCNT - cyc < CYCLES_400);
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_400)
+ ;
}
#endif // NEO_KHZ400
#elif defined(TEENSYDUINO) && (defined(__IMXRT1052__) || defined(__IMXRT1062__))
-#define CYCLES_800_T0H (F_CPU_ACTUAL / 4000000)
-#define CYCLES_800_T1H (F_CPU_ACTUAL / 1250000)
-#define CYCLES_800 (F_CPU_ACTUAL / 800000)
-#define CYCLES_400_T0H (F_CPU_ACTUAL / 2000000)
-#define CYCLES_400_T1H (F_CPU_ACTUAL / 833333)
-#define CYCLES_400 (F_CPU_ACTUAL / 400000)
-
- uint8_t *p = pixels,
- *end = p + numBytes, pix, mask;
- volatile uint32_t *set = portSetRegister(pin),
- *clr = portClearRegister(pin);
- uint32_t cyc,
- msk = digitalPinToBitMask(pin);
-
- ARM_DEMCR |= ARM_DEMCR_TRCENA;
+#define CYCLES_800_T0H (F_CPU_ACTUAL / 4000000)
+#define CYCLES_800_T1H (F_CPU_ACTUAL / 1250000)
+#define CYCLES_800 (F_CPU_ACTUAL / 800000)
+#define CYCLES_400_T0H (F_CPU_ACTUAL / 2000000)
+#define CYCLES_400_T1H (F_CPU_ACTUAL / 833333)
+#define CYCLES_400 (F_CPU_ACTUAL / 400000)
+
+ uint8_t *p = pixels, *end = p + numBytes, pix, mask;
+ volatile uint32_t *set = portSetRegister(pin), *clr = portClearRegister(pin);
+ uint32_t cyc, msk = digitalPinToBitMask(pin);
+
+ ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
cyc = ARM_DWT_CYCCNT + CYCLES_800;
- while(p < end) {
+ while (p < end) {
pix = *p++;
- for(mask = 0x80; mask; mask >>= 1) {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_800);
- cyc = ARM_DWT_CYCCNT;
+ for (mask = 0x80; mask; mask >>= 1) {
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
+ ;
+ cyc = ARM_DWT_CYCCNT;
*set = msk;
- if(pix & mask) {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H);
+ if (pix & mask) {
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H)
+ ;
} else {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H);
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H)
+ ;
}
*clr = msk;
}
}
- while(ARM_DWT_CYCCNT - cyc < CYCLES_800);
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
+ ;
#if defined(NEO_KHZ400)
} else { // 400 kHz bitstream
cyc = ARM_DWT_CYCCNT + CYCLES_400;
- while(p < end) {
+ while (p < end) {
pix = *p++;
- for(mask = 0x80; mask; mask >>= 1) {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_400);
- cyc = ARM_DWT_CYCCNT;
+ for (mask = 0x80; mask; mask >>= 1) {
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_400)
+ ;
+ cyc = ARM_DWT_CYCCNT;
*set = msk;
- if(pix & mask) {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H);
+ if (pix & mask) {
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H)
+ ;
} else {
- while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H);
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H)
+ ;
}
*clr = msk;
}
}
- while(ARM_DWT_CYCCNT - cyc < CYCLES_400);
+ while (ARM_DWT_CYCCNT - cyc < CYCLES_400)
+ ;
}
#endif // NEO_KHZ400
#elif defined(TEENSYDUINO) && defined(__MKL26Z64__) // Teensy-LC
#if F_CPU == 48000000
- uint8_t *p = pixels,
- pix, count, dly,
- bitmask = digitalPinToBitMask(pin);
+ uint8_t *p = pixels, pix, count, dly, bitmask = digitalPinToBitMask(pin);
volatile uint8_t *reg = portSetRegister(pin);
- uint32_t num = numBytes;
- asm volatile(
- "L%=_begin:" "\n\t"
- "ldrb %[pix], [%[p], #0]" "\n\t"
- "lsl %[pix], #24" "\n\t"
- "movs %[count], #7" "\n\t"
- "L%=_loop:" "\n\t"
- "lsl %[pix], #1" "\n\t"
- "bcs L%=_loop_one" "\n\t"
- "L%=_loop_zero:" "\n\t"
- "strb %[bitmask], [%[reg], #0]" "\n\t"
- "movs %[dly], #4" "\n\t"
- "L%=_loop_delay_T0H:" "\n\t"
- "sub %[dly], #1" "\n\t"
- "bne L%=_loop_delay_T0H" "\n\t"
- "strb %[bitmask], [%[reg], #4]" "\n\t"
- "movs %[dly], #13" "\n\t"
- "L%=_loop_delay_T0L:" "\n\t"
- "sub %[dly], #1" "\n\t"
- "bne L%=_loop_delay_T0L" "\n\t"
- "b L%=_next" "\n\t"
- "L%=_loop_one:" "\n\t"
- "strb %[bitmask], [%[reg], #0]" "\n\t"
- "movs %[dly], #13" "\n\t"
- "L%=_loop_delay_T1H:" "\n\t"
- "sub %[dly], #1" "\n\t"
- "bne L%=_loop_delay_T1H" "\n\t"
- "strb %[bitmask], [%[reg], #4]" "\n\t"
- "movs %[dly], #4" "\n\t"
- "L%=_loop_delay_T1L:" "\n\t"
- "sub %[dly], #1" "\n\t"
- "bne L%=_loop_delay_T1L" "\n\t"
- "nop" "\n\t"
- "L%=_next:" "\n\t"
- "sub %[count], #1" "\n\t"
- "bne L%=_loop" "\n\t"
- "lsl %[pix], #1" "\n\t"
- "bcs L%=_last_one" "\n\t"
- "L%=_last_zero:" "\n\t"
- "strb %[bitmask], [%[reg], #0]" "\n\t"
- "movs %[dly], #4" "\n\t"
- "L%=_last_delay_T0H:" "\n\t"
- "sub %[dly], #1" "\n\t"
- "bne L%=_last_delay_T0H" "\n\t"
- "strb %[bitmask], [%[reg], #4]" "\n\t"
- "movs %[dly], #10" "\n\t"
- "L%=_last_delay_T0L:" "\n\t"
- "sub %[dly], #1" "\n\t"
- "bne L%=_last_delay_T0L" "\n\t"
- "b L%=_repeat" "\n\t"
- "L%=_last_one:" "\n\t"
- "strb %[bitmask], [%[reg], #0]" "\n\t"
- "movs %[dly], #13" "\n\t"
- "L%=_last_delay_T1H:" "\n\t"
- "sub %[dly], #1" "\n\t"
- "bne L%=_last_delay_T1H" "\n\t"
- "strb %[bitmask], [%[reg], #4]" "\n\t"
- "movs %[dly], #1" "\n\t"
- "L%=_last_delay_T1L:" "\n\t"
- "sub %[dly], #1" "\n\t"
- "bne L%=_last_delay_T1L" "\n\t"
- "nop" "\n\t"
- "L%=_repeat:" "\n\t"
- "add %[p], #1" "\n\t"
- "sub %[num], #1" "\n\t"
- "bne L%=_begin" "\n\t"
- "L%=_done:" "\n\t"
- : [p] "+r" (p),
- [pix] "=&r" (pix),
- [count] "=&r" (count),
- [dly] "=&r" (dly),
- [num] "+r" (num)
- : [bitmask] "r" (bitmask),
- [reg] "r" (reg)
- );
+ uint32_t num = numBytes;
+ asm volatile("L%=_begin:"
+ "\n\t"
+ "ldrb %[pix], [%[p], #0]"
+ "\n\t"
+ "lsl %[pix], #24"
+ "\n\t"
+ "movs %[count], #7"
+ "\n\t"
+ "L%=_loop:"
+ "\n\t"
+ "lsl %[pix], #1"
+ "\n\t"
+ "bcs L%=_loop_one"
+ "\n\t"
+ "L%=_loop_zero:"
+ "\n\t"
+ "strb %[bitmask], [%[reg], #0]"
+ "\n\t"
+ "movs %[dly], #4"
+ "\n\t"
+ "L%=_loop_delay_T0H:"
+ "\n\t"
+ "sub %[dly], #1"
+ "\n\t"
+ "bne L%=_loop_delay_T0H"
+ "\n\t"
+ "strb %[bitmask], [%[reg], #4]"
+ "\n\t"
+ "movs %[dly], #13"
+ "\n\t"
+ "L%=_loop_delay_T0L:"
+ "\n\t"
+ "sub %[dly], #1"
+ "\n\t"
+ "bne L%=_loop_delay_T0L"
+ "\n\t"
+ "b L%=_next"
+ "\n\t"
+ "L%=_loop_one:"
+ "\n\t"
+ "strb %[bitmask], [%[reg], #0]"
+ "\n\t"
+ "movs %[dly], #13"
+ "\n\t"
+ "L%=_loop_delay_T1H:"
+ "\n\t"
+ "sub %[dly], #1"
+ "\n\t"
+ "bne L%=_loop_delay_T1H"
+ "\n\t"
+ "strb %[bitmask], [%[reg], #4]"
+ "\n\t"
+ "movs %[dly], #4"
+ "\n\t"
+ "L%=_loop_delay_T1L:"
+ "\n\t"
+ "sub %[dly], #1"
+ "\n\t"
+ "bne L%=_loop_delay_T1L"
+ "\n\t"
+ "nop"
+ "\n\t"
+ "L%=_next:"
+ "\n\t"
+ "sub %[count], #1"
+ "\n\t"
+ "bne L%=_loop"
+ "\n\t"
+ "lsl %[pix], #1"
+ "\n\t"
+ "bcs L%=_last_one"
+ "\n\t"
+ "L%=_last_zero:"
+ "\n\t"
+ "strb %[bitmask], [%[reg], #0]"
+ "\n\t"
+ "movs %[dly], #4"
+ "\n\t"
+ "L%=_last_delay_T0H:"
+ "\n\t"
+ "sub %[dly], #1"
+ "\n\t"
+ "bne L%=_last_delay_T0H"
+ "\n\t"
+ "strb %[bitmask], [%[reg], #4]"
+ "\n\t"
+ "movs %[dly], #10"
+ "\n\t"
+ "L%=_last_delay_T0L:"
+ "\n\t"
+ "sub %[dly], #1"
+ "\n\t"
+ "bne L%=_last_delay_T0L"
+ "\n\t"
+ "b L%=_repeat"
+ "\n\t"
+ "L%=_last_one:"
+ "\n\t"
+ "strb %[bitmask], [%[reg], #0]"
+ "\n\t"
+ "movs %[dly], #13"
+ "\n\t"
+ "L%=_last_delay_T1H:"
+ "\n\t"
+ "sub %[dly], #1"
+ "\n\t"
+ "bne L%=_last_delay_T1H"
+ "\n\t"
+ "strb %[bitmask], [%[reg], #4]"
+ "\n\t"
+ "movs %[dly], #1"
+ "\n\t"
+ "L%=_last_delay_T1L:"
+ "\n\t"
+ "sub %[dly], #1"
+ "\n\t"
+ "bne L%=_last_delay_T1L"
+ "\n\t"
+ "nop"
+ "\n\t"
+ "L%=_repeat:"
+ "\n\t"
+ "add %[p], #1"
+ "\n\t"
+ "sub %[num], #1"
+ "\n\t"
+ "bne L%=_begin"
+ "\n\t"
+ "L%=_done:"
+ "\n\t"
+ : [p] "+r"(p), [pix] "=&r"(pix), [count] "=&r"(count),
+ [dly] "=&r"(dly), [num] "+r"(num)
+ : [bitmask] "r"(bitmask), [reg] "r"(reg));
#else
#error "Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz"
#endif // F_CPU == 48000000
-// Begin of support for nRF52 based boards -------------------------
+ // Begin of support for nRF52 based boards -------------------------
#elif defined(NRF52) || defined(NRF52_SERIES)
// [[[Begin of the Neopixel NRF52 EasyDMA implementation
@@ -1386,16 +1977,16 @@ void Adafruit_NeoPixel::show(void) {
//#define MAGIC_T1H 12UL | (0x8000) // 0.75us
// WS2812B (rev B) timing is 0.4 and 0.8 us
-#define MAGIC_T0H 6UL | (0x8000) // 0.375us
-#define MAGIC_T1H 13UL | (0x8000) // 0.8125us
+#define MAGIC_T0H 6UL | (0x8000) // 0.375us
+#define MAGIC_T1H 13UL | (0x8000) // 0.8125us
// WS2811 (400 khz) timing is 0.5 and 1.2
-#define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us
-#define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us
+#define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us
+#define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us
// For 400Khz, we double value of CTOPVAL
-#define CTOPVAL 20UL // 1.25us
-#define CTOPVAL_400KHz 40UL // 2.5us
+#define CTOPVAL 20UL // 1.25us
+#define CTOPVAL_400KHz 40UL // 2.5us
// ---------- END Constants for the EasyDMA implementation -------------
//
@@ -1407,14 +1998,14 @@ void Adafruit_NeoPixel::show(void) {
// The number of cycles was hand picked and is guaranteed to be 100%
// organic to preserve freshness and high accuracy.
// ---------- BEGIN Constants for cycle counter implementation ---------
-#define CYCLES_800_T0H 18 // ~0.36 uS
-#define CYCLES_800_T1H 41 // ~0.76 uS
-#define CYCLES_800 71 // ~1.25 uS
+#define CYCLES_800_T0H 18 // ~0.36 uS
+#define CYCLES_800_T1H 41 // ~0.76 uS
+#define CYCLES_800 71 // ~1.25 uS
-#define CYCLES_400_T0H 26 // ~0.50 uS
-#define CYCLES_400_T1H 70 // ~1.26 uS
-#define CYCLES_400 156 // ~2.50 uS
-// ---------- END of Constants for cycle counter implementation --------
+#define CYCLES_400_T0H 26 // ~0.50 uS
+#define CYCLES_400_T1H 70 // ~1.26 uS
+#define CYCLES_400 156 // ~2.50 uS
+ // ---------- END of Constants for cycle counter implementation --------
// To support both the SoftDevice + Neopixels we use the EasyDMA
// feature from the NRF25. However this technique implies to
@@ -1426,55 +2017,60 @@ void Adafruit_NeoPixel::show(void) {
//
// If there is not enough memory, we will fall back to cycle counter
// using DWT
- uint32_t pattern_size = numBytes*8*sizeof(uint16_t)+2*sizeof(uint16_t);
- uint16_t* pixels_pattern = NULL;
+ uint32_t pattern_size =
+ numBytes * 8 * sizeof(uint16_t) + 2 * sizeof(uint16_t);
+ uint16_t *pixels_pattern = NULL;
- NRF_PWM_Type* pwm = NULL;
+ NRF_PWM_Type *pwm = NULL;
// Try to find a free PWM device, which is not enabled
// and has no connected pins
- NRF_PWM_Type* PWM[] = {
- NRF_PWM0, NRF_PWM1, NRF_PWM2
+ NRF_PWM_Type *PWM[] = {
+ NRF_PWM0,
+ NRF_PWM1,
+ NRF_PWM2
#if defined(NRF_PWM3)
- ,NRF_PWM3
+ ,
+ NRF_PWM3
#endif
};
- for(unsigned int device = 0; device < (sizeof(PWM)/sizeof(PWM[0])); device++) {
- if( (PWM[device]->ENABLE == 0) &&
+ for (unsigned int device = 0; device < (sizeof(PWM) / sizeof(PWM[0]));
+ device++) {
+ if ((PWM[device]->ENABLE == 0) &&
(PWM[device]->PSEL.OUT[0] & PWM_PSEL_OUT_CONNECT_Msk) &&
(PWM[device]->PSEL.OUT[1] & PWM_PSEL_OUT_CONNECT_Msk) &&
(PWM[device]->PSEL.OUT[2] & PWM_PSEL_OUT_CONNECT_Msk) &&
- (PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk)
- ) {
+ (PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk)) {
pwm = PWM[device];
break;
}
}
// only malloc if there is PWM device available
- if ( pwm != NULL ) {
- #if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe malloc
- pixels_pattern = (uint16_t *) rtos_malloc(pattern_size);
- #else
- pixels_pattern = (uint16_t *) malloc(pattern_size);
- #endif
+ if (pwm != NULL) {
+#if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe malloc
+ pixels_pattern = (uint16_t *)rtos_malloc(pattern_size);
+#else
+ pixels_pattern = (uint16_t *)malloc(pattern_size);
+#endif
}
// Use the identified device to choose the implementation
// If a PWM device is available use DMA
- if( (pixels_pattern != NULL) && (pwm != NULL) ) {
+ if ((pixels_pattern != NULL) && (pwm != NULL)) {
uint16_t pos = 0; // bit position
- for(uint16_t n=0; n0; mask >>= 1) {
- #if defined(NEO_KHZ400)
- if( !is800KHz ) {
- pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz;
- }else
- #endif
+ for (uint8_t mask = 0x80; mask > 0; mask >>= 1) {
+#if defined(NEO_KHZ400)
+ if (!is800KHz) {
+ pixels_pattern[pos] =
+ (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz;
+ } else
+#endif
{
pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H;
}
@@ -1491,15 +2087,16 @@ void Adafruit_NeoPixel::show(void) {
pwm->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
// Set the PWM to use the 16MHz clock
- pwm->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
+ pwm->PRESCALER =
+ (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
// Setting of the maximum count
// but keeping it on 16Mhz allows for more granularity just
// in case someone wants to do more fine-tuning of the timing.
#if defined(NEO_KHZ400)
- if( !is800KHz ) {
+ if (!is800KHz) {
pwm->COUNTERTOP = (CTOPVAL_400KHz << PWM_COUNTERTOP_COUNTERTOP_Pos);
- }else
+ } else
#endif
{
pwm->COUNTERTOP = (CTOPVAL << PWM_COUNTERTOP_COUNTERTOP_Pos);
@@ -1518,10 +2115,10 @@ void Adafruit_NeoPixel::show(void) {
pwm->SEQ[0].PTR = (uint32_t)(pixels_pattern) << PWM_SEQ_PTR_PTR_Pos;
// Calculation of the number of steps loaded from memory.
- pwm->SEQ[0].CNT = (pattern_size/sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos;
+ pwm->SEQ[0].CNT = (pattern_size / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos;
// The following settings are ignored with the current config.
- pwm->SEQ[0].REFRESH = 0;
+ pwm->SEQ[0].REFRESH = 0;
pwm->SEQ[0].ENDDELAY = 0;
// The Neopixel implementation is a blocking algorithm. DMA
@@ -1529,29 +2126,28 @@ void Adafruit_NeoPixel::show(void) {
// operation we enable the interruption for the end of sequence
// and block the execution thread until the event flag is set by
// the peripheral.
-// pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled<INTEN |= (PWM_INTEN_SEQEND0_Enabled<PSEL.OUT[0] = g_APinDescription[pin].name;
- #else
+#else
pwm->PSEL.OUT[0] = g_ADigitalPinMap[pin];
- #endif
+#endif
// Enable the PWM
pwm->ENABLE = 1;
// After all of this and many hours of reading the documentation
// we are ready to start the sequence...
- pwm->EVENTS_SEQEND[0] = 0;
+ pwm->EVENTS_SEQEND[0] = 0;
pwm->TASKS_SEQSTART[0] = 1;
// But we have to wait for the flag to be set.
- while(!pwm->EVENTS_SEQEND[0])
- {
- #if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_NRF52840)
+ while (!pwm->EVENTS_SEQEND[0]) {
+#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_NRF52840)
yield();
- #endif
+#endif
}
// Before leave we clear the flag for the event.
@@ -1565,40 +2161,39 @@ void Adafruit_NeoPixel::show(void) {
pwm->PSEL.OUT[0] = 0xFFFFFFFFUL;
- #if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe free
- rtos_free(pixels_pattern);
- #else
- free(pixels_pattern);
- #endif
- }// End of DMA implementation
+#if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe free
+ rtos_free(pixels_pattern);
+#else
+ free(pixels_pattern);
+#endif
+ } // End of DMA implementation
// ---------------------------------------------------------------------
- else{
-#ifndef ARDUINO_ARCH_NRF52840
- // Fall back to DWT
- #if defined(ARDUINO_NRF52_ADAFRUIT)
- // Bluefruit Feather 52 uses freeRTOS
- // Critical Section is used since it does not block SoftDevice execution
- taskENTER_CRITICAL();
- #elif defined(NRF52_DISABLE_INT)
- // If you are using the Bluetooth SoftDevice we advise you to not disable
- // the interrupts. Disabling the interrupts even for short periods of time
- // causes the SoftDevice to stop working.
- // Disable the interrupts only in cases where you need high performance for
- // the LEDs and if you are not using the EasyDMA feature.
- __disable_irq();
- #endif
-
- NRF_GPIO_Type* nrf_port = (NRF_GPIO_Type*) digitalPinToPort(pin);
+ else {
+#ifndef ARDUINO_ARCH_NRF52840
+// Fall back to DWT
+#if defined(ARDUINO_NRF52_ADAFRUIT)
+ // Bluefruit Feather 52 uses freeRTOS
+ // Critical Section is used since it does not block SoftDevice execution
+ taskENTER_CRITICAL();
+#elif defined(NRF52_DISABLE_INT)
+ // If you are using the Bluetooth SoftDevice we advise you to not disable
+ // the interrupts. Disabling the interrupts even for short periods of time
+ // causes the SoftDevice to stop working.
+ // Disable the interrupts only in cases where you need high performance for
+ // the LEDs and if you are not using the EasyDMA feature.
+ __disable_irq();
+#endif
+
+ NRF_GPIO_Type *nrf_port = (NRF_GPIO_Type *)digitalPinToPort(pin);
uint32_t pinMask = digitalPinToBitMask(pin);
- uint32_t CYCLES_X00 = CYCLES_800;
+ uint32_t CYCLES_X00 = CYCLES_800;
uint32_t CYCLES_X00_T1H = CYCLES_800_T1H;
uint32_t CYCLES_X00_T0H = CYCLES_800_T0H;
#if defined(NEO_KHZ400)
- if( !is800KHz )
- {
- CYCLES_X00 = CYCLES_400;
+ if (!is800KHz) {
+ CYCLES_X00 = CYCLES_400;
CYCLES_X00_T1H = CYCLES_400_T1H;
CYCLES_X00_T0H = CYCLES_400_T0H;
}
@@ -1609,36 +2204,39 @@ void Adafruit_NeoPixel::show(void) {
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
// Tries to re-send the frame if is interrupted by the SoftDevice.
- while(1) {
+ while (1) {
uint8_t *p = pixels;
uint32_t cycStart = DWT->CYCCNT;
uint32_t cyc = 0;
- for(uint16_t n=0; n>= 1) {
- while(DWT->CYCCNT - cyc < CYCLES_X00);
- cyc = DWT->CYCCNT;
+ for (uint8_t mask = 0x80; mask; mask >>= 1) {
+ while (DWT->CYCCNT - cyc < CYCLES_X00)
+ ;
+ cyc = DWT->CYCCNT;
nrf_port->OUTSET |= pinMask;
- if(pix & mask) {
- while(DWT->CYCCNT - cyc < CYCLES_X00_T1H);
+ if (pix & mask) {
+ while (DWT->CYCCNT - cyc < CYCLES_X00_T1H)
+ ;
} else {
- while(DWT->CYCCNT - cyc < CYCLES_X00_T0H);
+ while (DWT->CYCCNT - cyc < CYCLES_X00_T0H)
+ ;
}
nrf_port->OUTCLR |= pinMask;
}
}
- while(DWT->CYCCNT - cyc < CYCLES_X00);
-
+ while (DWT->CYCCNT - cyc < CYCLES_X00)
+ ;
// If total time longer than 25%, resend the whole data.
// Since we are likely to be interrupted by SoftDevice
- if ( (DWT->CYCCNT - cycStart) < ( 8*numBytes*((CYCLES_X00*5)/4) ) ) {
+ if ((DWT->CYCCNT - cycStart) < (8 * numBytes * ((CYCLES_X00 * 5) / 4))) {
break;
}
@@ -1646,40 +2244,44 @@ void Adafruit_NeoPixel::show(void) {
delayMicroseconds(300);
}
- // Enable interrupts again
- #if defined(ARDUINO_NRF52_ADAFRUIT)
- taskEXIT_CRITICAL();
- #elif defined(NRF52_DISABLE_INT)
- __enable_irq();
- #endif
+// Enable interrupts again
+#if defined(ARDUINO_NRF52_ADAFRUIT)
+ taskEXIT_CRITICAL();
+#elif defined(NRF52_DISABLE_INT)
+ __enable_irq();
+#endif
#endif
}
-// END of NRF52 implementation
+ // END of NRF52 implementation
-#elif defined (__SAMD21E17A__) || defined(__SAMD21G18A__) || defined(__SAMD21E18A__) || defined(__SAMD21J18A__) // Arduino Zero, Gemma/Trinket M0, SODAQ Autonomo and others
+#elif defined(__SAMD21E17A__) || defined(__SAMD21G18A__) || \
+ defined(__SAMD21E18A__) || defined(__SAMD21J18A__) || \
+ defined (__SAMD11C14A__)
+ // Arduino Zero, Gemma/Trinket M0, SODAQ Autonomo
+ // and others
// Tried this with a timer/counter, couldn't quite get adequate
// resolution. So yay, you get a load of goofball NOPs...
- uint8_t *ptr, *end, p, bitMask, portNum;
- uint32_t pinMask;
+ uint8_t *ptr, *end, p, bitMask, portNum;
+ uint32_t pinMask;
- portNum = g_APinDescription[pin].ulPort;
- pinMask = 1ul << g_APinDescription[pin].ulPin;
- ptr = pixels;
- end = ptr + numBytes;
- p = *ptr++;
- bitMask = 0x80;
+ portNum = g_APinDescription[pin].ulPort;
+ pinMask = 1ul << g_APinDescription[pin].ulPin;
+ ptr = pixels;
+ end = ptr + numBytes;
+ p = *ptr++;
+ bitMask = 0x80;
volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg),
*clr = &(PORT->Group[portNum].OUTCLR.reg);
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
- for(;;) {
+ for (;;) {
*set = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop;");
- if(p & bitMask) {
+ if (p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
@@ -1690,20 +2292,21 @@ void Adafruit_NeoPixel::show(void) {
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
}
- if(bitMask >>= 1) {
+ if (bitMask >>= 1) {
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;");
} else {
- if(ptr >= end) break;
- p = *ptr++;
+ if (ptr >= end)
+ break;
+ p = *ptr++;
bitMask = 0x80;
}
}
#if defined(NEO_KHZ400)
} else { // 400 KHz bitstream
- for(;;) {
+ for (;;) {
*set = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
- if(p & bitMask) {
+ if (p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
@@ -1720,29 +2323,195 @@ void Adafruit_NeoPixel::show(void) {
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
- if(bitMask >>= 1) {
+ if (bitMask >>= 1) {
asm("nop; nop; nop; nop; nop; nop; nop;");
} else {
- if(ptr >= end) break;
- p = *ptr++;
+ if (ptr >= end)
+ break;
+ p = *ptr++;
bitMask = 0x80;
}
}
}
#endif
-#elif defined (__SAMD51__) // M4
+//----
+#elif defined(XMC1100_XMC2GO) || defined(XMC1100_H_BRIDGE2GO) || defined(XMC1100_Boot_Kit) || defined(XMC1300_Boot_Kit)
+
+ // XMC1100/1200/1300 with ARM Cortex M0 are running with 32MHz, XMC1400 runs with 48MHz so may not work
+ // Tried this with a timer/counter, couldn't quite get adequate
+ // resolution. So yay, you get a load of goofball NOPs...
- uint8_t *ptr, *end, p, bitMask, portNum, bit;
+ uint8_t *ptr, *end, p, bitMask, portNum;
uint32_t pinMask;
- portNum = g_APinDescription[pin].ulPort;
- pinMask = 1ul << g_APinDescription[pin].ulPin;
ptr = pixels;
end = ptr + numBytes;
p = *ptr++;
bitMask = 0x80;
+ XMC_GPIO_PORT_t* XMC_port = mapping_port_pin[ pin ].port;
+ uint8_t XMC_pin = mapping_port_pin[ pin ].pin;
+
+ uint32_t omrhigh = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_HIGH << XMC_pin;
+ uint32_t omrlow = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_LOW << XMC_pin;
+
+#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled
+ if(is800KHz) {
+#endif
+ for(;;) {
+ XMC_port->OMR = omrhigh;
+ asm("nop; nop; nop; nop;");
+ if(p & bitMask) {
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop;");
+ XMC_port->OMR = omrlow;
+ } else {
+ XMC_port->OMR = omrlow;
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop;");
+ }
+ if(bitMask >>= 1) {
+ asm("nop; nop; nop; nop; nop;");
+ } else {
+ if(ptr >= end) break;
+ p = *ptr++;
+ bitMask = 0x80;
+ }
+ }
+#ifdef NEO_KHZ400 // untested code
+ } else { // 400 KHz bitstream
+ for(;;) {
+ XMC_port->OMR = omrhigh;
+ asm("nop; nop; nop; nop; nop;");
+ if(p & bitMask) {
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop;");
+ XMC_port->OMR = omrlow;
+ } else {
+ XMC_port->OMR = omrlow;
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop;");
+ }
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;");
+ if(bitMask >>= 1) {
+ asm("nop; nop; nop;");
+ } else {
+ if(ptr >= end) break;
+ p = *ptr++;
+ bitMask = 0x80;
+ }
+ }
+ }
+
+#endif
+//----
+
+//----
+#elif defined(XMC4700_Relax_Kit) || defined(XMC4800_Relax_Kit)
+
+// XMC4700 and XMC4800 with ARM Cortex M4 are running with 144MHz
+// Tried this with a timer/counter, couldn't quite get adequate
+// resolution. So yay, you get a load of goofball NOPs...
+
+uint8_t *ptr, *end, p, bitMask, portNum;
+uint32_t pinMask;
+
+ptr = pixels;
+end = ptr + numBytes;
+p = *ptr++;
+bitMask = 0x80;
+
+XMC_GPIO_PORT_t* XMC_port = mapping_port_pin[ pin ].port;
+uint8_t XMC_pin = mapping_port_pin[ pin ].pin;
+
+uint32_t omrhigh = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_HIGH << XMC_pin;
+uint32_t omrlow = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_LOW << XMC_pin;
+
+#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled
+if(is800KHz) {
+#endif
+
+ for(;;) {
+ XMC_port->OMR = omrhigh;
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop;");
+ if(p & bitMask) {
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;");
+ XMC_port->OMR = omrlow;
+ } else {
+ XMC_port->OMR = omrlow;
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;");
+ }
+ if(bitMask >>= 1) {
+ asm("nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;"
+ "nop; nop; nop; nop; nop; nop; nop; nop;");
+ } else {
+ if(ptr >= end) break;
+ p = *ptr++;
+ bitMask = 0x80;
+ }
+ }
+
+
+#ifdef NEO_KHZ400
+ } else { // 400 KHz bitstream
+ // ToDo!
+ }
+#endif
+//----
+
+#elif defined(__SAMD51__) // M4
+
+ uint8_t *ptr, *end, p, bitMask, portNum, bit;
+ uint32_t pinMask;
+
+ portNum = g_APinDescription[pin].ulPort;
+ pinMask = 1ul << g_APinDescription[pin].ulPin;
+ ptr = pixels;
+ end = ptr + numBytes;
+ p = *ptr++;
+ bitMask = 0x80;
+
volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg),
*clr = &(PORT->Group[portNum].OUTCLR.reg);
@@ -1762,65 +2531,67 @@ void Adafruit_NeoPixel::show(void) {
// seems to work just well enough. When finished, the SysTick
// peripheral is set back to its original state.
- uint32_t t0, t1, top, ticks,
- saveLoad = SysTick->LOAD, saveVal = SysTick->VAL;
+ uint32_t t0, t1, top, ticks, saveLoad = SysTick->LOAD, saveVal = SysTick->VAL;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
- top = (uint32_t)(F_CPU * 0.00000125); // Bit hi + lo = 1.25 uS
- t0 = top - (uint32_t)(F_CPU * 0.00000040); // 0 = 0.4 uS hi
- t1 = top - (uint32_t)(F_CPU * 0.00000080); // 1 = 0.8 uS hi
+ top = (uint32_t)(F_CPU * 0.00000125); // Bit hi + lo = 1.25 uS
+ t0 = top - (uint32_t)(F_CPU * 0.00000040); // 0 = 0.4 uS hi
+ t1 = top - (uint32_t)(F_CPU * 0.00000080); // 1 = 0.8 uS hi
#if defined(NEO_KHZ400)
- } else { // 400 KHz bitstream
- top = (uint32_t)(F_CPU * 0.00000250); // Bit hi + lo = 2.5 uS
- t0 = top - (uint32_t)(F_CPU * 0.00000050); // 0 = 0.5 uS hi
- t1 = top - (uint32_t)(F_CPU * 0.00000120); // 1 = 1.2 uS hi
+ } else { // 400 KHz bitstream
+ top = (uint32_t)(F_CPU * 0.00000250); // Bit hi + lo = 2.5 uS
+ t0 = top - (uint32_t)(F_CPU * 0.00000050); // 0 = 0.5 uS hi
+ t1 = top - (uint32_t)(F_CPU * 0.00000120); // 1 = 1.2 uS hi
}
#endif
- SysTick->LOAD = top; // Config SysTick for NeoPixel bit freq
- SysTick->VAL = top; // Set to start value (counts down)
- (void)SysTick->VAL; // Dummy read helps sync up 1st bit
+ SysTick->LOAD = top; // Config SysTick for NeoPixel bit freq
+ SysTick->VAL = top; // Set to start value (counts down)
+ (void)SysTick->VAL; // Dummy read helps sync up 1st bit
- for(;;) {
- *set = pinMask; // Set output high
+ for (;;) {
+ *set = pinMask; // Set output high
ticks = (p & bitMask) ? t1 : t0; // SysTick threshold,
- while(SysTick->VAL > ticks); // wait for it
- *clr = pinMask; // Set output low
- if(!(bitMask >>= 1)) { // Next bit for this byte...done?
- if(ptr >= end) break; // If last byte sent, exit loop
- p = *ptr++; // Fetch next byte
- bitMask = 0x80; // Reset bitmask
+ while (SysTick->VAL > ticks)
+ ; // wait for it
+ *clr = pinMask; // Set output low
+ if (!(bitMask >>= 1)) { // Next bit for this byte...done?
+ if (ptr >= end)
+ break; // If last byte sent, exit loop
+ p = *ptr++; // Fetch next byte
+ bitMask = 0x80; // Reset bitmask
}
- while(SysTick->VAL <= ticks); // Wait for rollover to 'top'
+ while (SysTick->VAL <= ticks)
+ ; // Wait for rollover to 'top'
}
- SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms
- SysTick->VAL = saveVal; // Restore SysTick value
+ SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms
+ SysTick->VAL = saveVal; // Restore SysTick value
-#elif defined (ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz)
+#elif defined(ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz)
// Tried this with a timer/counter, couldn't quite get adequate
// resolution. So yay, you get a load of goofball NOPs...
- uint8_t *ptr, *end, p, bitMask;
- uint32_t pinMask;
+ uint8_t *ptr, *end, p, bitMask;
+ uint32_t pinMask;
- pinMask = BIT(PIN_MAP[pin].gpio_bit);
- ptr = pixels;
- end = ptr + numBytes;
- p = *ptr++;
- bitMask = 0x80;
+ pinMask = BIT(PIN_MAP[pin].gpio_bit);
+ ptr = pixels;
+ end = ptr + numBytes;
+ p = *ptr++;
+ bitMask = 0x80;
volatile uint16_t *set = &(PIN_MAP[pin].gpio_device->regs->BSRRL);
volatile uint16_t *clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH);
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
- for(;;) {
- if(p & bitMask) { // ONE
+ for (;;) {
+ if (p & bitMask) { // ONE
// High 800ns
*set = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
@@ -1866,12 +2637,13 @@ void Adafruit_NeoPixel::show(void) {
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
}
- if(bitMask >>= 1) {
+ if (bitMask >>= 1) {
// Move on to the next pixel
asm("nop;");
} else {
- if(ptr >= end) break;
- p = *ptr++;
+ if (ptr >= end)
+ break;
+ p = *ptr++;
bitMask = 0x80;
}
}
@@ -1882,17 +2654,17 @@ void Adafruit_NeoPixel::show(void) {
#endif
#elif defined(TARGET_LPC1768)
- uint8_t *ptr, *end, p, bitMask;
- ptr = pixels;
- end = ptr + numBytes;
- p = *ptr++;
- bitMask = 0x80;
+ uint8_t *ptr, *end, p, bitMask;
+ ptr = pixels;
+ end = ptr + numBytes;
+ p = *ptr++;
+ bitMask = 0x80;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
- for(;;) {
- if(p & bitMask) {
+ for (;;) {
+ if (p & bitMask) {
// data ONE high
// min: 550 typ: 700 max: 5,500
gpio_set(pin);
@@ -1910,12 +2682,13 @@ void Adafruit_NeoPixel::show(void) {
gpio_clear(pin);
time::delay_ns(450);
}
- if(bitMask >>= 1) {
+ if (bitMask >>= 1) {
// Move on to the next pixel
asm("nop;");
} else {
- if(ptr >= end) break;
- p = *ptr++;
+ if (ptr >= end)
+ break;
+ p = *ptr++;
bitMask = 0x80;
}
}
@@ -1925,204 +2698,229 @@ void Adafruit_NeoPixel::show(void) {
}
#endif
#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32)
- uint8_t *p = pixels, *end = p + numBytes,
- pix = *p++, mask = 0x80;
- uint32_t cyc;
+ uint8_t *p = pixels, *end = p + numBytes, pix = *p++, mask = 0x80;
+ uint32_t cyc;
uint32_t saveLoad = SysTick->LOAD, saveVal = SysTick->VAL;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
- uint32_t top = (F_CPU / 800000); // 1.25µs
- uint32_t t0 = top - (F_CPU / 2500000); // 0.4µs
- uint32_t t1 = top - (F_CPU / 1250000); // 0.8µs
+ uint32_t top = (F_CPU / 800000); // 1.25µs
+ uint32_t t0 = top - (F_CPU / 2500000); // 0.4µs
+ uint32_t t1 = top - (F_CPU / 1250000); // 0.8µs
SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq
- SysTick->VAL = 0; // Set to start value
- for(;;) {
+ SysTick->VAL = 0; // Set to start value
+ for (;;) {
LL_GPIO_SetOutputPin(gpioPort, gpioPin);
cyc = (pix & mask) ? t1 : t0;
- while(SysTick->VAL > cyc);
+ while (SysTick->VAL > cyc)
+ ;
LL_GPIO_ResetOutputPin(gpioPort, gpioPin);
- if(!(mask >>= 1)) {
- if(p >= end) break;
- pix = *p++;
+ if (!(mask >>= 1)) {
+ if (p >= end)
+ break;
+ pix = *p++;
mask = 0x80;
}
- while(SysTick->VAL <= cyc);
+ while (SysTick->VAL <= cyc)
+ ;
}
#if defined(NEO_KHZ400)
- } else { // 400 kHz bitstream
- uint32_t top = (F_CPU / 400000); // 2.5µs
- uint32_t t0 = top - (F_CPU / 2000000); // 0.5µs
- uint32_t t1 = top - (F_CPU / 833333); // 1.2µs
+ } else { // 400 kHz bitstream
+ uint32_t top = (F_CPU / 400000); // 2.5µs
+ uint32_t t0 = top - (F_CPU / 2000000); // 0.5µs
+ uint32_t t1 = top - (F_CPU / 833333); // 1.2µs
SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq
- SysTick->VAL = 0; // Set to start value
- for(;;) {
+ SysTick->VAL = 0; // Set to start value
+ for (;;) {
LL_GPIO_SetOutputPin(gpioPort, gpioPin);
cyc = (pix & mask) ? t1 : t0;
- while(SysTick->VAL > cyc);
+ while (SysTick->VAL > cyc)
+ ;
LL_GPIO_ResetOutputPin(gpioPort, gpioPin);
- if(!(mask >>= 1)) {
- if(p >= end) break;
- pix = *p++;
+ if (!(mask >>= 1)) {
+ if (p >= end)
+ break;
+ pix = *p++;
mask = 0x80;
}
- while(SysTick->VAL <= cyc);
+ while (SysTick->VAL <= cyc)
+ ;
}
}
#endif // NEO_KHZ400
- SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms
- SysTick->VAL = saveVal; // Restore SysTick value
-#elif defined (NRF51)
- uint8_t *p = pixels,
- pix, count, mask;
- int32_t num = numBytes;
- unsigned int bitmask = ( 1 << g_ADigitalPinMap[pin] );
-// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/variants/BBCmicrobit/variant.cpp
+ SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms
+ SysTick->VAL = saveVal; // Restore SysTick value
+#elif defined(NRF51)
+ uint8_t *p = pixels, pix, count, mask;
+ int32_t num = numBytes;
+ unsigned int bitmask = (1 << g_ADigitalPinMap[pin]);
+ // https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/variants/BBCmicrobit/variant.cpp
- volatile unsigned int *reg = (unsigned int *) (0x50000000UL + 0x508);
+ volatile unsigned int *reg = (unsigned int *)(0x50000000UL + 0x508);
-// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/cores/nRF5/SDK/components/device/nrf51.h
-// http://www.iot-programmer.com/index.php/books/27-micro-bit-iot-in-c/chapters-micro-bit-iot-in-c/47-micro-bit-iot-in-c-fast-memory-mapped-gpio?showall=1
-// https://github.com/Microsoft/pxt-neopixel/blob/master/sendbuffer.asm
+ // https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/cores/nRF5/SDK/components/device/nrf51.h
+ // http://www.iot-programmer.com/index.php/books/27-micro-bit-iot-in-c/chapters-micro-bit-iot-in-c/47-micro-bit-iot-in-c-fast-memory-mapped-gpio?showall=1
+ // https://github.com/Microsoft/pxt-neopixel/blob/master/sendbuffer.asm
asm volatile(
- // "cpsid i" ; disable irq
-
- // b .start
- "b L%=_start" "\n\t"
- // .nextbit: ; C0
- "L%=_nextbit:" "\n\t" //; C0
- // str r1, [r3, #0] ; pin := hi C2
- "strb %[bitmask], [%[reg], #0]" "\n\t" //; pin := hi C2
- // tst r6, r0 ; C3
- "tst %[mask], %[pix]" "\n\t"// ; C3
- // bne .islate ; C4
- "bne L%=_islate" "\n\t" //; C4
- // str r1, [r2, #0] ; pin := lo C6
- "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C6
- // .islate:
- "L%=_islate:" "\n\t"
- // lsrs r6, r6, #1 ; r6 >>= 1 C7
- "lsr %[mask], %[mask], #1" "\n\t" //; r6 >>= 1 C7
- // bne .justbit ; C8
- "bne L%=_justbit" "\n\t" //; C8
-
- // ; not just a bit - need new byte
- // adds r4, #1 ; r4++ C9
- "add %[p], #1" "\n\t" //; r4++ C9
- // subs r5, #1 ; r5-- C10
- "sub %[num], #1" "\n\t" //; r5-- C10
- // bcc .stop ; if (r5<0) goto .stop C11
- "bcc L%=_stop" "\n\t" //; if (r5<0) goto .stop C11
- // .start:
- "L%=_start:"
- // movs r6, #0x80 ; reset mask C12
- "movs %[mask], #0x80" "\n\t" //; reset mask C12
- // nop ; C13
- "nop" "\n\t" //; C13
-
- // .common: ; C13
- "L%=_common:" "\n\t" //; C13
- // str r1, [r2, #0] ; pin := lo C15
- "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C15
- // ; always re-load byte - it just fits with the cycles better this way
- // ldrb r0, [r4, #0] ; r0 := *r4 C17
- "ldrb %[pix], [%[p], #0]" "\n\t" //; r0 := *r4 C17
- // b .nextbit ; C20
- "b L%=_nextbit" "\n\t" //; C20
-
- // .justbit: ; C10
- "L%=_justbit:" "\n\t" //; C10
- // ; no nops, branch taken is already 3 cycles
- // b .common ; C13
- "b L%=_common" "\n\t" //; C13
-
- // .stop:
- "L%=_stop:" "\n\t"
- // str r1, [r2, #0] ; pin := lo
- "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo
- // cpsie i ; enable irq
-
- : [p] "+r" (p),
- [pix] "=&r" (pix),
- [count] "=&r" (count),
- [mask] "=&r" (mask),
- [num] "+r" (num)
- : [bitmask] "r" (bitmask),
- [reg] "r" (reg)
- );
+ // "cpsid i" ; disable irq
+
+ // b .start
+ "b L%=_start"
+ "\n\t"
+ // .nextbit: ; C0
+ "L%=_nextbit:"
+ "\n\t" //; C0
+ // str r1, [r3, #0] ; pin := hi C2
+ "strb %[bitmask], [%[reg], #0]"
+ "\n\t" //; pin := hi C2
+ // tst r6, r0 ; C3
+ "tst %[mask], %[pix]"
+ "\n\t" // ; C3
+ // bne .islate ; C4
+ "bne L%=_islate"
+ "\n\t" //; C4
+ // str r1, [r2, #0] ; pin := lo C6
+ "strb %[bitmask], [%[reg], #4]"
+ "\n\t" //; pin := lo C6
+ // .islate:
+ "L%=_islate:"
+ "\n\t"
+ // lsrs r6, r6, #1 ; r6 >>= 1 C7
+ "lsr %[mask], %[mask], #1"
+ "\n\t" //; r6 >>= 1 C7
+ // bne .justbit ; C8
+ "bne L%=_justbit"
+ "\n\t" //; C8
+
+ // ; not just a bit - need new byte
+ // adds r4, #1 ; r4++ C9
+ "add %[p], #1"
+ "\n\t" //; r4++ C9
+ // subs r5, #1 ; r5-- C10
+ "sub %[num], #1"
+ "\n\t" //; r5-- C10
+ // bcc .stop ; if (r5<0) goto .stop C11
+ "bcc L%=_stop"
+ "\n\t" //; if (r5<0) goto .stop C11
+ // .start:
+ "L%=_start:"
+ // movs r6, #0x80 ; reset mask C12
+ "movs %[mask], #0x80"
+ "\n\t" //; reset mask C12
+ // nop ; C13
+ "nop"
+ "\n\t" //; C13
+
+ // .common: ; C13
+ "L%=_common:"
+ "\n\t" //; C13
+ // str r1, [r2, #0] ; pin := lo C15
+ "strb %[bitmask], [%[reg], #4]"
+ "\n\t" //; pin := lo C15
+ // ; always re-load byte - it just fits with the cycles better this way
+ // ldrb r0, [r4, #0] ; r0 := *r4 C17
+ "ldrb %[pix], [%[p], #0]"
+ "\n\t" //; r0 := *r4 C17
+ // b .nextbit ; C20
+ "b L%=_nextbit"
+ "\n\t" //; C20
+
+ // .justbit: ; C10
+ "L%=_justbit:"
+ "\n\t" //; C10
+ // ; no nops, branch taken is already 3 cycles
+ // b .common ; C13
+ "b L%=_common"
+ "\n\t" //; C13
+
+ // .stop:
+ "L%=_stop:"
+ "\n\t"
+ // str r1, [r2, #0] ; pin := lo
+ "strb %[bitmask], [%[reg], #4]"
+ "\n\t" //; pin := lo
+ // cpsie i ; enable irq
+
+ : [p] "+r"(p), [pix] "=&r"(pix), [count] "=&r"(count), [mask] "=&r"(mask),
+ [num] "+r"(num)
+ : [bitmask] "r"(bitmask), [reg] "r"(reg));
#elif defined(__SAM3X8E__) // Arduino Due
- #define SCALE VARIANT_MCK / 2UL / 1000000UL
- #define INST (2UL * F_CPU / VARIANT_MCK)
- #define TIME_800_0 ((int)(0.40 * SCALE + 0.5) - (5 * INST))
- #define TIME_800_1 ((int)(0.80 * SCALE + 0.5) - (5 * INST))
- #define PERIOD_800 ((int)(1.25 * SCALE + 0.5) - (5 * INST))
- #define TIME_400_0 ((int)(0.50 * SCALE + 0.5) - (5 * INST))
- #define TIME_400_1 ((int)(1.20 * SCALE + 0.5) - (5 * INST))
- #define PERIOD_400 ((int)(2.50 * SCALE + 0.5) - (5 * INST))
-
- int pinMask, time0, time1, period, t;
- Pio *port;
+#define SCALE VARIANT_MCK / 2UL / 1000000UL
+#define INST (2UL * F_CPU / VARIANT_MCK)
+#define TIME_800_0 ((int)(0.40 * SCALE + 0.5) - (5 * INST))
+#define TIME_800_1 ((int)(0.80 * SCALE + 0.5) - (5 * INST))
+#define PERIOD_800 ((int)(1.25 * SCALE + 0.5) - (5 * INST))
+#define TIME_400_0 ((int)(0.50 * SCALE + 0.5) - (5 * INST))
+#define TIME_400_1 ((int)(1.20 * SCALE + 0.5) - (5 * INST))
+#define PERIOD_400 ((int)(2.50 * SCALE + 0.5) - (5 * INST))
+
+ int pinMask, time0, time1, period, t;
+ Pio *port;
volatile WoReg *portSet, *portClear, *timeValue, *timeReset;
- uint8_t *p, *end, pix, mask;
+ uint8_t *p, *end, pix, mask;
pmc_set_writeprotect(false);
pmc_enable_periph_clk((uint32_t)TC3_IRQn);
TC_Configure(TC1, 0,
- TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1);
+ TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1);
TC_Start(TC1, 0);
- pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into
- port = g_APinDescription[pin].pPort; // declarations above. Want to
- portSet = &(port->PIO_SODR); // burn a few cycles after
- portClear = &(port->PIO_CODR); // starting timer to minimize
- timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'.
+ pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into
+ port = g_APinDescription[pin].pPort; // declarations above. Want to
+ portSet = &(port->PIO_SODR); // burn a few cycles after
+ portClear = &(port->PIO_CODR); // starting timer to minimize
+ timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'.
timeReset = &(TC1->TC_CHANNEL[0].TC_CCR);
- p = pixels;
- end = p + numBytes;
- pix = *p++;
- mask = 0x80;
+ p = pixels;
+ end = p + numBytes;
+ pix = *p++;
+ mask = 0x80;
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
- if(is800KHz) {
+ if (is800KHz) {
#endif
- time0 = TIME_800_0;
- time1 = TIME_800_1;
+ time0 = TIME_800_0;
+ time1 = TIME_800_1;
period = PERIOD_800;
#if defined(NEO_KHZ400)
} else { // 400 KHz bitstream
- time0 = TIME_400_0;
- time1 = TIME_400_1;
+ time0 = TIME_400_0;
+ time1 = TIME_400_1;
period = PERIOD_400;
}
#endif
- for(t = time0;; t = time0) {
- if(pix & mask) t = time1;
- while(*timeValue < (unsigned)period);
- *portSet = pinMask;
+ for (t = time0;; t = time0) {
+ if (pix & mask)
+ t = time1;
+ while (*timeValue < (unsigned)period)
+ ;
+ *portSet = pinMask;
*timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG;
- while(*timeValue < (unsigned)t);
+ while (*timeValue < (unsigned)t)
+ ;
*portClear = pinMask;
- if(!(mask >>= 1)) { // This 'inside-out' loop logic utilizes
- if(p >= end) break; // idle time to minimize inter-byte delays.
+ if (!(mask >>= 1)) { // This 'inside-out' loop logic utilizes
+ if (p >= end)
+ break; // idle time to minimize inter-byte delays.
pix = *p++;
mask = 0x80;
}
}
- while(*timeValue < (unsigned)period); // Wait for last bit
+ while (*timeValue < (unsigned)period)
+ ; // Wait for last bit
TC_Stop(TC1, 0);
#endif // end Due
-// END ARM ----------------------------------------------------------------
-
+ // END ARM ----------------------------------------------------------------
#elif defined(ESP8266) || defined(ESP32)
-// ESP8266 ----------------------------------------------------------------
+ // ESP8266 ----------------------------------------------------------------
// ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution
espShow(pin, pixels, numBytes, is800KHz);
@@ -2130,94 +2928,97 @@ void Adafruit_NeoPixel::show(void) {
#elif defined(KENDRYTE_K210)
k210Show(pin, pixels, numBytes, is800KHz);
-
-#elif defined(__ARDUINO_ARC__)
-// Arduino 101 -----------------------------------------------------------
+#elif defined(__ARDUINO_ARC__)
-#define NOPx7 { __builtin_arc_nop(); \
- __builtin_arc_nop(); __builtin_arc_nop(); \
- __builtin_arc_nop(); __builtin_arc_nop(); \
- __builtin_arc_nop(); __builtin_arc_nop(); }
+ // Arduino 101 -----------------------------------------------------------
+
+#define NOPx7 \
+ { \
+ __builtin_arc_nop(); \
+ __builtin_arc_nop(); \
+ __builtin_arc_nop(); \
+ __builtin_arc_nop(); \
+ __builtin_arc_nop(); \
+ __builtin_arc_nop(); \
+ __builtin_arc_nop(); \
+ }
PinDescription *pindesc = &g_APinDescription[pin];
- register uint32_t loop = 8 * numBytes; // one loop to handle all bytes and all bits
+ register uint32_t loop =
+ 8 * numBytes; // one loop to handle all bytes and all bits
register uint8_t *p = pixels;
- register uint32_t currByte = (uint32_t) (*p);
+ register uint32_t currByte = (uint32_t)(*p);
register uint32_t currBit = 0x80 & currByte;
register uint32_t bitCounter = 0;
register uint32_t first = 1;
- // The loop is unusual. Very first iteration puts all the way LOW to the wire -
- // constant LOW does not affect NEOPIXEL, so there is no visible effect displayed.
- // During that very first iteration CPU caches instructions in the loop.
- // Because of the caching process, "CPU slows down". NEOPIXEL pulse is very time sensitive
- // that's why we let the CPU cache first and we start regular pulse from 2nd iteration
+ // The loop is unusual. Very first iteration puts all the way LOW to the wire
+ // - constant LOW does not affect NEOPIXEL, so there is no visible effect
+ // displayed. During that very first iteration CPU caches instructions in the
+ // loop. Because of the caching process, "CPU slows down". NEOPIXEL pulse is
+ // very time sensitive that's why we let the CPU cache first and we start
+ // regular pulse from 2nd iteration
if (pindesc->ulGPIOType == SS_GPIO) {
register uint32_t reg = pindesc->ulGPIOBase + SS_GPIO_SWPORTA_DR;
uint32_t reg_val = __builtin_arc_lr((volatile uint32_t)reg);
register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId);
- register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId);
+ register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId);
loop += 1; // include first, special iteration
- while(loop--) {
- if(!first) {
+ while (loop--) {
+ if (!first) {
currByte <<= 1;
bitCounter++;
}
// 1 is >550ns high and >450ns low; 0 is 200..500ns high and >450ns low
- __builtin_arc_sr(first ? reg_bit_low : reg_bit_high, (volatile uint32_t)reg);
- if(currBit) { // ~400ns HIGH (740ns overall)
- NOPx7
- NOPx7
+ __builtin_arc_sr(first ? reg_bit_low : reg_bit_high,
+ (volatile uint32_t)reg);
+ if (currBit) { // ~400ns HIGH (740ns overall)
+ NOPx7 NOPx7
}
// ~340ns HIGH
- NOPx7
- __builtin_arc_nop();
+ NOPx7 __builtin_arc_nop();
// 820ns LOW; per spec, max allowed low here is 5000ns */
__builtin_arc_sr(reg_bit_low, (volatile uint32_t)reg);
- NOPx7
- NOPx7
+ NOPx7 NOPx7
- if(bitCounter >= 8) {
+ if (bitCounter >= 8) {
bitCounter = 0;
- currByte = (uint32_t) (*++p);
+ currByte = (uint32_t)(*++p);
}
currBit = 0x80 & currByte;
first = 0;
}
- } else if(pindesc->ulGPIOType == SOC_GPIO) {
+ } else if (pindesc->ulGPIOType == SOC_GPIO) {
register uint32_t reg = pindesc->ulGPIOBase + SOC_GPIO_SWPORTA_DR;
uint32_t reg_val = MMIO_REG_VAL(reg);
register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId);
- register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId);
+ register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId);
loop += 1; // include first, special iteration
- while(loop--) {
- if(!first) {
+ while (loop--) {
+ if (!first) {
currByte <<= 1;
bitCounter++;
}
MMIO_REG_VAL(reg) = first ? reg_bit_low : reg_bit_high;
- if(currBit) { // ~430ns HIGH (740ns overall)
- NOPx7
- NOPx7
- __builtin_arc_nop();
+ if (currBit) { // ~430ns HIGH (740ns overall)
+ NOPx7 NOPx7 __builtin_arc_nop();
}
// ~310ns HIGH
NOPx7
- // 850ns LOW; per spec, max allowed low here is 5000ns */
- MMIO_REG_VAL(reg) = reg_bit_low;
- NOPx7
- NOPx7
+ // 850ns LOW; per spec, max allowed low here is 5000ns */
+ MMIO_REG_VAL(reg) = reg_bit_low;
+ NOPx7 NOPx7
- if(bitCounter >= 8) {
+ if (bitCounter >= 8) {
bitCounter = 0;
- currByte = (uint32_t) (*++p);
+ currByte = (uint32_t)(*++p);
}
currBit = 0x80 & currByte;
@@ -2229,10 +3030,9 @@ void Adafruit_NeoPixel::show(void) {
#error Architecture not supported
#endif
+ // END ARCHITECTURE SELECT ------------------------------------------------
-// END ARCHITECTURE SELECT ------------------------------------------------
-
-#if !( defined(NRF52) || defined(NRF52_SERIES) )
+#if !(defined(NRF52) || defined(NRF52_SERIES) || defined(ESP32))
interrupts();
#endif
@@ -2244,15 +3044,16 @@ void Adafruit_NeoPixel::show(void) {
if any, is set to INPUT and the new pin is set to OUTPUT.
@param p Arduino pin number (-1 = no pin).
*/
-void Adafruit_NeoPixel::setPin(uint16_t p) {
- if(begun && (pin >= 0)) pinMode(pin, INPUT);
+void Adafruit_NeoPixel::setPin(int16_t p) {
+ if (begun && (pin >= 0))
+ pinMode(pin, INPUT); // Disable existing out pin
pin = p;
- if(begun) {
+ if (begun) {
pinMode(p, OUTPUT);
digitalWrite(p, LOW);
}
#if defined(__AVR__)
- port = portOutputRegister(digitalPinToPort(p));
+ port = portOutputRegister(digitalPinToPort(p));
pinMask = digitalPinToBitMask(p);
#endif
#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32)
@@ -2269,23 +3070,23 @@ void Adafruit_NeoPixel::setPin(uint16_t p) {
@param g Green brightness, 0 = minimum (off), 255 = maximum.
@param b Blue brightness, 0 = minimum (off), 255 = maximum.
*/
-void Adafruit_NeoPixel::setPixelColor(
- uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
+void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g,
+ uint8_t b) {
- if(n < numLEDs) {
- if(brightness) { // See notes in setBrightness()
+ if (n < numLEDs) {
+ if (brightness) { // See notes in setBrightness()
r = (r * brightness) >> 8;
g = (g * brightness) >> 8;
b = (b * brightness) >> 8;
}
uint8_t *p;
- if(wOffset == rOffset) { // Is an RGB-type strip
- p = &pixels[n * 3]; // 3 bytes per pixel
- } else { // Is a WRGB-type strip
- p = &pixels[n * 4]; // 4 bytes per pixel
- p[wOffset] = 0; // But only R,G,B passed -- set W to 0
+ if (wOffset == rOffset) { // Is an RGB-type strip
+ p = &pixels[n * 3]; // 3 bytes per pixel
+ } else { // Is a WRGB-type strip
+ p = &pixels[n * 4]; // 4 bytes per pixel
+ p[wOffset] = 0; // But only R,G,B passed -- set W to 0
}
- p[rOffset] = r; // R,G,B always stored
+ p[rOffset] = r; // R,G,B always stored
p[gOffset] = g;
p[bOffset] = b;
}
@@ -2301,24 +3102,24 @@ void Adafruit_NeoPixel::setPixelColor(
@param w White brightness, 0 = minimum (off), 255 = maximum, ignored
if using RGB pixels.
*/
-void Adafruit_NeoPixel::setPixelColor(
- uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
+void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g,
+ uint8_t b, uint8_t w) {
- if(n < numLEDs) {
- if(brightness) { // See notes in setBrightness()
+ if (n < numLEDs) {
+ if (brightness) { // See notes in setBrightness()
r = (r * brightness) >> 8;
g = (g * brightness) >> 8;
b = (b * brightness) >> 8;
w = (w * brightness) >> 8;
}
uint8_t *p;
- if(wOffset == rOffset) { // Is an RGB-type strip
- p = &pixels[n * 3]; // 3 bytes per pixel (ignore W)
- } else { // Is a WRGB-type strip
- p = &pixels[n * 4]; // 4 bytes per pixel
- p[wOffset] = w; // Store W
+ if (wOffset == rOffset) { // Is an RGB-type strip
+ p = &pixels[n * 3]; // 3 bytes per pixel (ignore W)
+ } else { // Is a WRGB-type strip
+ p = &pixels[n * 4]; // 4 bytes per pixel
+ p[wOffset] = w; // Store W
}
- p[rOffset] = r; // Store R,G,B
+ p[rOffset] = r; // Store R,G,B
p[gOffset] = g;
p[bOffset] = b;
}
@@ -2332,17 +3133,14 @@ void Adafruit_NeoPixel::setPixelColor(
and least significant byte is blue.
*/
void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) {
- if(n < numLEDs) {
- uint8_t *p,
- r = (uint8_t)(c >> 16),
- g = (uint8_t)(c >> 8),
- b = (uint8_t)c;
- if(brightness) { // See notes in setBrightness()
+ if (n < numLEDs) {
+ uint8_t *p, r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c;
+ if (brightness) { // See notes in setBrightness()
r = (r * brightness) >> 8;
g = (g * brightness) >> 8;
b = (b * brightness) >> 8;
}
- if(wOffset == rOffset) {
+ if (wOffset == rOffset) {
p = &pixels[n * 3];
} else {
p = &pixels[n * 4];
@@ -2369,21 +3167,22 @@ void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) {
void Adafruit_NeoPixel::fill(uint32_t c, uint16_t first, uint16_t count) {
uint16_t i, end;
- if(first >= numLEDs) {
+ if (first >= numLEDs) {
return; // If first LED is past end of strip, nothing to do
}
// Calculate the index ONE AFTER the last pixel to fill
- if(count == 0) {
+ if (count == 0) {
// Fill to end of strip
end = numLEDs;
} else {
// Ensure that the loop won't go past the last pixel
end = first + count;
- if(end > numLEDs) end = numLEDs;
+ if (end > numLEDs)
+ end = numLEDs;
}
- for(i = first; i < end; i++) {
+ for (i = first; i < end; i++) {
this->setPixelColor(i, c);
}
}
@@ -2446,45 +3245,45 @@ uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
// the constants below are not the multiples of 256 you might expect.
// Convert hue to R,G,B (nested ifs faster than divide+mod+switch):
- if(hue < 510) { // Red to Green-1
+ if (hue < 510) { // Red to Green-1
b = 0;
- if(hue < 255) { // Red to Yellow-1
+ if (hue < 255) { // Red to Yellow-1
r = 255;
- g = hue; // g = 0 to 254
- } else { // Yellow to Green-1
- r = 510 - hue; // r = 255 to 1
+ g = hue; // g = 0 to 254
+ } else { // Yellow to Green-1
+ r = 510 - hue; // r = 255 to 1
g = 255;
}
- } else if(hue < 1020) { // Green to Blue-1
+ } else if (hue < 1020) { // Green to Blue-1
r = 0;
- if(hue < 765) { // Green to Cyan-1
+ if (hue < 765) { // Green to Cyan-1
g = 255;
- b = hue - 510; // b = 0 to 254
- } else { // Cyan to Blue-1
- g = 1020 - hue; // g = 255 to 1
+ b = hue - 510; // b = 0 to 254
+ } else { // Cyan to Blue-1
+ g = 1020 - hue; // g = 255 to 1
b = 255;
}
- } else if(hue < 1530) { // Blue to Red-1
+ } else if (hue < 1530) { // Blue to Red-1
g = 0;
- if(hue < 1275) { // Blue to Magenta-1
- r = hue - 1020; // r = 0 to 254
+ if (hue < 1275) { // Blue to Magenta-1
+ r = hue - 1020; // r = 0 to 254
b = 255;
- } else { // Magenta to Red-1
+ } else { // Magenta to Red-1
r = 255;
- b = 1530 - hue; // b = 255 to 1
+ b = 1530 - hue; // b = 255 to 1
}
- } else { // Last 0.5 Red (quicker than % operator)
+ } else { // Last 0.5 Red (quicker than % operator)
r = 255;
g = b = 0;
}
// Apply saturation and value to R,G,B, pack into 32-bit result:
- uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
- uint16_t s1 = 1 + sat; // 1 to 256; same reason
- uint8_t s2 = 255 - sat; // 255 to 0
+ uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
+ uint16_t s1 = 1 + sat; // 1 to 256; same reason
+ uint8_t s2 = 255 - sat; // 255 to 0
return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) |
- (((((g * s1) >> 8) + s2) * v1) & 0xff00) |
- ( ((((b * s1) >> 8) + s2) * v1) >> 8);
+ (((((g * s1) >> 8) + s2) * v1) & 0xff00) |
+ (((((b * s1) >> 8) + s2) * v1) >> 8);
}
/*!
@@ -2499,44 +3298,41 @@ uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
This gets more pronounced at lower brightness levels.
*/
uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const {
- if(n >= numLEDs) return 0; // Out of bounds, return no color.
+ if (n >= numLEDs)
+ return 0; // Out of bounds, return no color.
uint8_t *p;
- if(wOffset == rOffset) { // Is RGB-type device
+ if (wOffset == rOffset) { // Is RGB-type device
p = &pixels[n * 3];
- if(brightness) {
+ if (brightness) {
// Stored color was decimated by setBrightness(). Returned value
// attempts to scale back to an approximation of the original 24-bit
// value used when setting the pixel color, but there will always be
// some error -- those bits are simply gone. Issue is most
// pronounced at low brightness levels.
return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) |
- (((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
- ( (uint32_t)(p[bOffset] << 8) / brightness );
+ (((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
+ ((uint32_t)(p[bOffset] << 8) / brightness);
} else {
// No brightness adjustment has been made -- return 'raw' color
- return ((uint32_t)p[rOffset] << 16) |
- ((uint32_t)p[gOffset] << 8) |
- (uint32_t)p[bOffset];
+ return ((uint32_t)p[rOffset] << 16) | ((uint32_t)p[gOffset] << 8) |
+ (uint32_t)p[bOffset];
}
- } else { // Is RGBW-type device
+ } else { // Is RGBW-type device
p = &pixels[n * 4];
- if(brightness) { // Return scaled color
+ if (brightness) { // Return scaled color
return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) |
(((uint32_t)(p[rOffset] << 8) / brightness) << 16) |
- (((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
- ( (uint32_t)(p[bOffset] << 8) / brightness );
+ (((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
+ ((uint32_t)(p[bOffset] << 8) / brightness);
} else { // Return raw color
- return ((uint32_t)p[wOffset] << 24) |
- ((uint32_t)p[rOffset] << 16) |
- ((uint32_t)p[gOffset] << 8) |
- (uint32_t)p[bOffset];
+ return ((uint32_t)p[wOffset] << 24) | ((uint32_t)p[rOffset] << 16) |
+ ((uint32_t)p[gOffset] << 8) | (uint32_t)p[bOffset];
}
}
}
-
/*!
@brief Adjust output brightness. Does not immediately affect what's
currently displayed on the LEDs. The next call to show() will
@@ -2560,7 +3356,7 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) {
// (color values are interpreted literally; no scaling), 1 = min
// brightness (off), 255 = just below max brightness.
uint8_t newBrightness = b + 1;
- if(newBrightness != brightness) { // Compare against prior value
+ if (newBrightness != brightness) { // Compare against prior value
// Brightness has changed -- re-scale existing data in RAM,
// This process is potentially "lossy," especially when increasing
// brightness. The tight timing in the WS2811/WS2812 code means there
@@ -2571,15 +3367,17 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) {
// the limited number of steps (quantization) in the old data will be
// quite visible in the re-scaled version. For a non-destructive
// change, you'll need to re-render the full strip data. C'est la vie.
- uint8_t c,
- *ptr = pixels,
- oldBrightness = brightness - 1; // De-wrap old brightness value
+ uint8_t c, *ptr = pixels,
+ oldBrightness = brightness - 1; // De-wrap old brightness value
uint16_t scale;
- if(oldBrightness == 0) scale = 0; // Avoid /0
- else if(b == 255) scale = 65535 / oldBrightness;
- else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
- for(uint16_t i=0; i> 8;
}
brightness = newBrightness;
@@ -2590,16 +3388,12 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) {
@brief Retrieve the last-set brightness value for the strip.
@return Brightness value: 0 = minimum (off), 255 = maximum.
*/
-uint8_t Adafruit_NeoPixel::getBrightness(void) const {
- return brightness - 1;
-}
+uint8_t Adafruit_NeoPixel::getBrightness(void) const { return brightness - 1; }
/*!
@brief Fill the whole NeoPixel strip with 0 / black / off.
*/
-void Adafruit_NeoPixel::clear(void) {
- memset(pixels, 0, numBytes);
-}
+void Adafruit_NeoPixel::clear(void) { memset(pixels, 0, numBytes); }
// A 32-bit variant of gamma8() that applies the same function
// to all components of a packed RGB or WRGB value.
@@ -2613,6 +3407,68 @@ uint32_t Adafruit_NeoPixel::gamma32(uint32_t x) {
// someone's storing information in the unused most significant byte
// of an RGB value, but this seems exceedingly rare and if it's
// encountered in reality they can mask values going in or coming out.
- for(uint8_t i=0; i<4; i++) y[i] = gamma8(y[i]);
+ for (uint8_t i = 0; i < 4; i++)
+ y[i] = gamma8(y[i]);
return x; // Packed 32-bit return
}
+
+/*!
+ @brief Fill NeoPixel strip with one or more cycles of hues.
+ Everyone loves the rainbow swirl so much, now it's canon!
+ @param first_hue Hue of first pixel, 0-65535, representing one full
+ cycle of the color wheel. Each subsequent pixel will
+ be offset to complete one or more cycles over the
+ length of the strip.
+ @param reps Number of cycles of the color wheel over the length
+ of the strip. Default is 1. Negative values can be
+ used to reverse the hue order.
+ @param saturation Saturation (optional), 0-255 = gray to pure hue,
+ default = 255.
+ @param brightness Brightness/value (optional), 0-255 = off to max,
+ default = 255. This is distinct and in combination
+ with any configured global strip brightness.
+ @param gammify If true (default), apply gamma correction to colors
+ for better appearance.
+*/
+void Adafruit_NeoPixel::rainbow(uint16_t first_hue, int8_t reps,
+ uint8_t saturation, uint8_t brightness, bool gammify) {
+ for (uint16_t i=0; i= 100)
- #include
- #else
- #include
- #include
- #endif
+#if (ARDUINO >= 100)
+#include
+#else
+#include
+#include
+#endif
+
+#ifdef USE_TINYUSB // For Serial when selecting TinyUSB
+#include
+#endif
+
#endif
#ifdef TARGET_LPC1768
- #include
+#include
+#endif
+
+#if defined(ARDUINO_ARCH_RP2040)
+#include
+#include "hardware/pio.h"
+#include "hardware/clocks.h"
+#include "rp2040_pio.h"
#endif
// The order of primary colors in the NeoPixel data stream can vary among
@@ -75,43 +87,43 @@
// 0bRRRRGGBB for RGB
// RGB NeoPixel permutations; white and red offsets are always same
-// Offset: W R G B
-#define NEO_RGB ((0<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B
-#define NEO_RBG ((0<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G
-#define NEO_GRB ((1<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B
-#define NEO_GBR ((2<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R
-#define NEO_BRG ((1<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G
-#define NEO_BGR ((2<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R
+// Offset: W R G B
+#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B
+#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G
+#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B
+#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R
+#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G
+#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R
// RGBW NeoPixel permutations; all 4 offsets are distinct
// Offset: W R G B
-#define NEO_WRGB ((0<<6) | (1<<4) | (2<<2) | (3)) ///< Transmit as W,R,G,B
-#define NEO_WRBG ((0<<6) | (1<<4) | (3<<2) | (2)) ///< Transmit as W,R,B,G
-#define NEO_WGRB ((0<<6) | (2<<4) | (1<<2) | (3)) ///< Transmit as W,G,R,B
-#define NEO_WGBR ((0<<6) | (3<<4) | (1<<2) | (2)) ///< Transmit as W,G,B,R
-#define NEO_WBRG ((0<<6) | (2<<4) | (3<<2) | (1)) ///< Transmit as W,B,R,G
-#define NEO_WBGR ((0<<6) | (3<<4) | (2<<2) | (1)) ///< Transmit as W,B,G,R
+#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3)) ///< Transmit as W,R,G,B
+#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2)) ///< Transmit as W,R,B,G
+#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3)) ///< Transmit as W,G,R,B
+#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2)) ///< Transmit as W,G,B,R
+#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1)) ///< Transmit as W,B,R,G
+#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1)) ///< Transmit as W,B,G,R
-#define NEO_RWGB ((1<<6) | (0<<4) | (2<<2) | (3)) ///< Transmit as R,W,G,B
-#define NEO_RWBG ((1<<6) | (0<<4) | (3<<2) | (2)) ///< Transmit as R,W,B,G
-#define NEO_RGWB ((2<<6) | (0<<4) | (1<<2) | (3)) ///< Transmit as R,G,W,B
-#define NEO_RGBW ((3<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B,W
-#define NEO_RBWG ((2<<6) | (0<<4) | (3<<2) | (1)) ///< Transmit as R,B,W,G
-#define NEO_RBGW ((3<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G,W
+#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3)) ///< Transmit as R,W,G,B
+#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2)) ///< Transmit as R,W,B,G
+#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3)) ///< Transmit as R,G,W,B
+#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B,W
+#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1)) ///< Transmit as R,B,W,G
+#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G,W
-#define NEO_GWRB ((1<<6) | (2<<4) | (0<<2) | (3)) ///< Transmit as G,W,R,B
-#define NEO_GWBR ((1<<6) | (3<<4) | (0<<2) | (2)) ///< Transmit as G,W,B,R
-#define NEO_GRWB ((2<<6) | (1<<4) | (0<<2) | (3)) ///< Transmit as G,R,W,B
-#define NEO_GRBW ((3<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B,W
-#define NEO_GBWR ((2<<6) | (3<<4) | (0<<2) | (1)) ///< Transmit as G,B,W,R
-#define NEO_GBRW ((3<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R,W
+#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3)) ///< Transmit as G,W,R,B
+#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2)) ///< Transmit as G,W,B,R
+#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3)) ///< Transmit as G,R,W,B
+#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B,W
+#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,W,R
+#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R,W
-#define NEO_BWRG ((1<<6) | (2<<4) | (3<<2) | (0)) ///< Transmit as B,W,R,G
-#define NEO_BWGR ((1<<6) | (3<<4) | (2<<2) | (0)) ///< Transmit as B,W,G,R
-#define NEO_BRWG ((2<<6) | (1<<4) | (3<<2) | (0)) ///< Transmit as B,R,W,G
-#define NEO_BRGW ((3<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G,W
-#define NEO_BGWR ((2<<6) | (3<<4) | (1<<2) | (0)) ///< Transmit as B,G,W,R
-#define NEO_BGRW ((3<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R,W
+#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0)) ///< Transmit as B,W,R,G
+#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0)) ///< Transmit as B,W,G,R
+#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0)) ///< Transmit as B,R,W,G
+#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G,W
+#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,W,R
+#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R,W
// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device.
// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is
@@ -134,7 +146,7 @@
#ifdef NEO_KHZ400
typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
#else
-typedef uint8_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
+typedef uint8_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
#endif
// These two tables are declared outside the Adafruit_NeoPixel class
@@ -149,22 +161,24 @@ for x in range(256):
if x&15 == 15: print
*/
static const uint8_t PROGMEM _NeoPixelSineTable[256] = {
- 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
- 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
- 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
- 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
- 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
- 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
- 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
- 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
- 128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
- 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
- 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
- 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
- 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
- 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
- 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
- 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124};
+ 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170,
+ 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211,
+ 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240,
+ 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254,
+ 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251,
+ 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232,
+ 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198,
+ 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155,
+ 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109,
+ 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65,
+ 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29,
+ 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6,
+ 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11,
+ 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37,
+ 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
+ 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121,
+ 124};
/* Similar to above, but for an 8-bit gamma-correction table.
Copy & paste this snippet into a Python REPL to regenerate:
@@ -175,49 +189,49 @@ for x in range(256):
if x&15 == 15: print
*/
static const uint8_t PROGMEM _NeoPixelGammaTable[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
- 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
- 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12,
- 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20,
- 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
- 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
- 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75,
- 76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96,
- 97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120,
- 122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148,
- 150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,
- 182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215,
- 218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255};
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,
+ 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
+ 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
+ 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17,
+ 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
+ 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35,
+ 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81,
+ 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 97, 99, 100, 102,
+ 103, 105, 106, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 124, 125,
+ 127, 129, 130, 132, 134, 136, 137, 139, 141, 143, 145, 146, 148, 150, 152,
+ 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182,
+ 184, 186, 188, 191, 193, 195, 197, 199, 202, 204, 206, 209, 211, 213, 215,
+ 218, 220, 223, 225, 227, 230, 232, 235, 237, 240, 242, 245, 247, 250, 252,
+ 255};
-/*!
+/*!
@brief Class that stores state and functions for interacting with
Adafruit NeoPixels and compatible devices.
*/
class Adafruit_NeoPixel {
- public:
-
+public:
// Constructor: number of LEDs, pin number, LED type
- Adafruit_NeoPixel(uint16_t n, uint16_t pin=6,
- neoPixelType type=NEO_GRB + NEO_KHZ800);
+ Adafruit_NeoPixel(uint16_t n, int16_t pin = 6,
+ neoPixelType type = NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel(void);
~Adafruit_NeoPixel();
- void begin(void);
- void show(void);
- void setPin(uint16_t p);
- void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
- void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b,
- uint8_t w);
- void setPixelColor(uint16_t n, uint32_t c);
- void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0);
- void setBrightness(uint8_t);
- void clear(void);
- void updateLength(uint16_t n);
- void updateType(neoPixelType t);
+ void begin(void);
+ void show(void);
+ void setPin(int16_t p);
+ void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
+ void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
+ void setPixelColor(uint16_t n, uint32_t c);
+ void fill(uint32_t c = 0, uint16_t first = 0, uint16_t count = 0);
+ void setBrightness(uint8_t);
+ void clear(void);
+ void updateLength(uint16_t n);
+ void updateType(neoPixelType t);
/*!
@brief Check whether a call to show() will start sending data
immediately or will 'block' for a required interval. NeoPixels
@@ -232,10 +246,26 @@ class Adafruit_NeoPixel {
if show() would block (meaning some idle time is available).
*/
bool canShow(void) {
- if (endTime > micros()) {
- endTime = micros();
+ // It's normal and possible for endTime to exceed micros() if the
+ // 32-bit clock counter has rolled over (about every 70 minutes).
+ // Since both are uint32_t, a negative delta correctly maps back to
+ // positive space, and it would seem like the subtraction below would
+ // suffice. But a problem arises if code invokes show() very
+ // infrequently...the micros() counter may roll over MULTIPLE times in
+ // that interval, the delta calculation is no longer correct and the
+ // next update may stall for a very long time. The check below resets
+ // the latch counter if a rollover has occurred. This can cause an
+ // extra delay of up to 300 microseconds in the rare case where a
+ // show() call happens precisely around the rollover, but that's
+ // neither likely nor especially harmful, vs. other code that might
+ // stall for 30+ minutes, or having to document and frequently remind
+ // and/or provide tech support explaining an unintuitive need for
+ // show() calls at least once an hour.
+ uint32_t now = micros();
+ if (endTime > now) {
+ endTime = now;
}
- return (micros() - endTime) >= 300L;
+ return (now - endTime) >= 300L;
}
/*!
@brief Get a pointer directly to the NeoPixel data buffer in RAM.
@@ -251,19 +281,19 @@ class Adafruit_NeoPixel {
writes past the ends of the buffer. Great power, great
responsibility and all that.
*/
- uint8_t *getPixels(void) const { return pixels; };
- uint8_t getBrightness(void) const;
+ uint8_t *getPixels(void) const { return pixels; };
+ uint8_t getBrightness(void) const;
/*!
@brief Retrieve the pin number used for NeoPixel data output.
@return Arduino pin number (-1 if not set).
*/
- int16_t getPin(void) const { return pin; };
+ int16_t getPin(void) const { return pin; };
/*!
@brief Return the number of pixels in an Adafruit_NeoPixel strip object.
@return Pixel count (0 if not set).
*/
- uint16_t numPixels(void) const { return numLEDs; }
- uint32_t getPixelColor(uint16_t n) const;
+ uint16_t numPixels(void) const { return numLEDs; }
+ uint32_t getPixelColor(uint16_t n) const;
/*!
@brief An 8-bit integer sine wave function, not directly compatible
with standard trigonometric units like radians or degrees.
@@ -276,7 +306,7 @@ class Adafruit_NeoPixel {
a signed int8_t, but you'll most likely want unsigned as this
output is often used for pixel brightness in animation effects.
*/
- static uint8_t sine8(uint8_t x) {
+ static uint8_t sine8(uint8_t x) {
return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out
}
/*!
@@ -290,7 +320,7 @@ class Adafruit_NeoPixel {
NeoPixels in average tasks. If you need finer control you'll
need to provide your own gamma-correction function instead.
*/
- static uint8_t gamma8(uint8_t x) {
+ static uint8_t gamma8(uint8_t x) {
return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out
}
/*!
@@ -304,8 +334,8 @@ class Adafruit_NeoPixel {
function. Packed RGB format is predictable, regardless of
LED strand color order.
*/
- static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
- return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+ static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
+ return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
/*!
@brief Convert separate red, green, blue and white values into a
@@ -319,10 +349,10 @@ class Adafruit_NeoPixel {
function. Packed WRGB format is predictable, regardless of
LED strand color order.
*/
- static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
- return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+ static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
+ return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
- static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255);
+ static uint32_t ColorHSV(uint16_t hue, uint8_t sat = 255, uint8_t val = 255);
/*!
@brief A gamma-correction function for 32-bit packed RGB or WRGB
colors. Makes color transitions appear more perceptially
@@ -335,32 +365,48 @@ class Adafruit_NeoPixel {
control you'll need to provide your own gamma-correction
function instead.
*/
- static uint32_t gamma32(uint32_t x);
+ static uint32_t gamma32(uint32_t x);
+
+ void rainbow(uint16_t first_hue = 0, int8_t reps = 1,
+ uint8_t saturation = 255, uint8_t brightness = 255,
+ bool gammify = true);
- protected:
+ static neoPixelType str2order(const char *v);
-#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled...
- bool is800KHz; ///< true if 800 KHz pixels
+private:
+#if defined(ARDUINO_ARCH_RP2040)
+ void rp2040Init(uint8_t pin, bool is800KHz);
+ void rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
#endif
- bool begun; ///< true if begin() previously called
- uint16_t numLEDs; ///< Number of RGB LEDs in strip
- uint16_t numBytes; ///< Size of 'pixels' buffer below
- int16_t pin; ///< Output pin number (-1 if not yet set)
- uint8_t brightness; ///< Strip brightness 0-255 (stored as +1)
- uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each)
- uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel
- uint8_t gOffset; ///< Index of green byte
- uint8_t bOffset; ///< Index of blue byte
- uint8_t wOffset; ///< Index of white (==rOffset if no white)
- uint32_t endTime; ///< Latch timing reference
+
+protected:
+#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled...
+ bool is800KHz; ///< true if 800 KHz pixels
+#endif
+ bool begun; ///< true if begin() previously called
+ uint16_t numLEDs; ///< Number of RGB LEDs in strip
+ uint16_t numBytes; ///< Size of 'pixels' buffer below
+ int16_t pin; ///< Output pin number (-1 if not yet set)
+ uint8_t brightness; ///< Strip brightness 0-255 (stored as +1)
+ uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each)
+ uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel
+ uint8_t gOffset; ///< Index of green byte
+ uint8_t bOffset; ///< Index of blue byte
+ uint8_t wOffset; ///< Index of white (==rOffset if no white)
+ uint32_t endTime; ///< Latch timing reference
#ifdef __AVR__
- volatile uint8_t *port; ///< Output PORT register
- uint8_t pinMask; ///< Output PORT bitmask
+ volatile uint8_t *port; ///< Output PORT register
+ uint8_t pinMask; ///< Output PORT bitmask
#endif
#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32)
- GPIO_TypeDef *gpioPort; ///< Output GPIO PORT
- uint32_t gpioPin; ///< Output GPIO PIN
+ GPIO_TypeDef *gpioPort; ///< Output GPIO PORT
+ uint32_t gpioPin; ///< Output GPIO PIN
+#endif
+#if defined(ARDUINO_ARCH_RP2040)
+ PIO pio = pio0;
+ int sm = 0;
+ bool init = true;
#endif
};
-#endif // ADAFRUIT_NEOPIXEL_H
+#endif // ADAFRUIT_NEOPIXEL_H
\ No newline at end of file
diff --git a/src/main/resources/resource/Arduino/MrlComm/MrlComm.ino b/src/main/resources/resource/Arduino/MrlComm/MrlComm.ino
index dbe6cd8e6f..7568dd625f 100644
--- a/src/main/resources/resource/Arduino/MrlComm/MrlComm.ino
+++ b/src/main/resources/resource/Arduino/MrlComm/MrlComm.ino
@@ -56,6 +56,8 @@
#include
#endif
+#define SERIAL_BUFFER_SIZE 256
+
/***********************************************************************
* GLOBAL VARIABLES
* TODO - work on reducing globals and pass as parameters
diff --git a/src/main/resources/resource/Arduino/MrlComm/MrlNeopixel.cpp b/src/main/resources/resource/Arduino/MrlComm/MrlNeopixel.cpp
index 4371f6a1f1..380e9d07f0 100755
--- a/src/main/resources/resource/Arduino/MrlComm/MrlNeopixel.cpp
+++ b/src/main/resources/resource/Arduino/MrlComm/MrlNeopixel.cpp
@@ -60,7 +60,7 @@ void MrlNeopixel::colorWipe()
void MrlNeopixel::scanner()
{
- if (y == strip->numPixels() - 1)
+ if (y >= strip->numPixels() - 1)
{
z = 0;
}
@@ -158,7 +158,7 @@ void MrlNeopixel::theaterChaseRainbow()
void MrlNeopixel::writeMatrix(byte bufferSize, const byte *buffer)
{
- if (!strip)
+ if (!strip || runAnimation)
{
return;
}
diff --git a/src/main/resources/resource/BoofCV/basket_depth.png b/src/main/resources/resource/BoofCV/basket_depth.png
new file mode 100644
index 0000000000..1477305355
Binary files /dev/null and b/src/main/resources/resource/BoofCV/basket_depth.png differ
diff --git a/src/main/resources/resource/BoofCV/basket_rgb.png b/src/main/resources/resource/BoofCV/basket_rgb.png
new file mode 100644
index 0000000000..0bf3d57021
Binary files /dev/null and b/src/main/resources/resource/BoofCV/basket_rgb.png differ
diff --git a/src/main/resources/resource/BoofCV/intrinsic.yaml b/src/main/resources/resource/BoofCV/intrinsic.yaml
new file mode 100644
index 0000000000..db7c7f9ca9
--- /dev/null
+++ b/src/main/resources/resource/BoofCV/intrinsic.yaml
@@ -0,0 +1,19 @@
+# Pinhole camera model with radial and tangential distortion
+# (fx,fy) = focal length, (cx,cy) = principle point, (width,height) = image shape
+# radial = radial distortion, (t1,t2) = tangential distortion
+
+pinhole:
+ fx: 529.74137370586
+ fy: 529.5715453060717
+ cx: 312.57382117058427
+ cy: 257.05061008728114
+ width: 640
+ height: 480
+ skew: 0.0
+model: pinhole_radial_tangential
+radial_tangential:
+ radial:
+ - 0.17889353480851655
+ - -0.32301207366192053
+ t1: 0.0
+ t2: 0.0
diff --git a/src/main/resources/resource/BoofCV/visualdepth.yaml b/src/main/resources/resource/BoofCV/visualdepth.yaml
new file mode 100644
index 0000000000..b911c9a497
--- /dev/null
+++ b/src/main/resources/resource/BoofCV/visualdepth.yaml
@@ -0,0 +1,20 @@
+# RGB Depth Camera Calibration
+model: visual_depth
+max_depth: 10000
+no_depth: 0
+intrinsic:
+ pinhole:
+ fx: 529.74137370586
+ fy: 529.5715453060717
+ cx: 312.57382117058427
+ cy: 257.05061008728114
+ width: 640
+ height: 480
+ skew: 0.0
+ model: pinhole_radial_tangential
+ radial_tangential:
+ radial:
+ - 0.17889353480851655
+ - -0.32301207366192053
+ t1: 0.0
+ t2: 0.0
\ No newline at end of file
diff --git a/src/main/resources/resource/Caliko.png b/src/main/resources/resource/Caliko.png
new file mode 100644
index 0000000000..3ddbc83695
Binary files /dev/null and b/src/main/resources/resource/Caliko.png differ
diff --git a/src/main/resources/resource/Caliko/pyramid.obj b/src/main/resources/resource/Caliko/pyramid.obj
new file mode 100644
index 0000000000..797d3ac9fe
--- /dev/null
+++ b/src/main/resources/resource/Caliko/pyramid.obj
@@ -0,0 +1,56 @@
+# Triangle consisting of 8 lines: A square at the bottom then from each vertex up to the tip
+
+
+# Top Left Back
+# v -0.5f 0.5f 0.0f
+# Top Right Back
+# v 0.5f 0.5f 0.0f
+# Bottom Left Back
+# v -0.5f -0.5f 0.0f
+# Bottom Right Back
+# v 0.5f -0.5f 0.0f
+# Center Front
+# v 0.0f 0.0f 1.0f
+
+# Top Left Back
+v -0.5f 0.5f 0.0f
+# Bottom Left Back
+v -0.5f -0.5f 0.0f
+
+# Top Left Back
+v -0.5f 0.5f 0.0f
+# Top Right Back
+v 0.5f 0.5f 0.0f
+
+# Top Right Back
+v 0.5f 0.5f 0.0f
+# Bottom Right Back
+v 0.5f -0.5f 0.0f
+
+# Bottom Right Back
+v 0.5f -0.5f 0.0f
+# Bottom Left Back
+v -0.5f -0.5f 0.0f
+
+
+
+# SQUARE
+# Top Left Back
+v -0.5f 0.5f 0.0f
+# Center Front
+v 0.0f 0.0f 1.0f
+
+# Top Right Back
+v 0.5f 0.5f 0.0f
+# Center Front
+v 0.0f 0.0f 1.0f
+
+# Bottom Left Back
+v -0.5f -0.5f 0.0f
+# Center Front
+v 0.0f 0.0f 1.0f
+
+# Bottom Right Back
+v 0.5f -0.5f 0.0f
+# Center Front
+v 0.0f 0.0f 1.0f
\ No newline at end of file
diff --git a/src/main/resources/resource/Docker.png b/src/main/resources/resource/Docker.png
new file mode 100644
index 0000000000..1f1fb12d24
Binary files /dev/null and b/src/main/resources/resource/Docker.png differ
diff --git a/src/main/resources/resource/Houston.png b/src/main/resources/resource/Houston.png
deleted file mode 100644
index 637e5e094f..0000000000
Binary files a/src/main/resources/resource/Houston.png and /dev/null differ
diff --git a/src/main/resources/resource/IPCamera.png b/src/main/resources/resource/IpCamera.png
similarity index 100%
rename from src/main/resources/resource/IPCamera.png
rename to src/main/resources/resource/IpCamera.png
diff --git a/src/main/resources/resource/OpenAI.png b/src/main/resources/resource/OpenAI.png
new file mode 100644
index 0000000000..2426491545
Binary files /dev/null and b/src/main/resources/resource/OpenAI.png differ
diff --git a/src/main/resources/resource/Py4j/OakD.py b/src/main/resources/resource/Py4j/OakD/OakD.py
similarity index 100%
rename from src/main/resources/resource/Py4j/OakD.py
rename to src/main/resources/resource/Py4j/OakD/OakD.py
diff --git a/src/main/resources/resource/Py4j/OakD/__init__.py b/src/main/resources/resource/Py4j/OakD/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/MultiMsgSync.py b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/MultiMsgSync.py
new file mode 100644
index 0000000000..c75fe307b1
--- /dev/null
+++ b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/MultiMsgSync.py
@@ -0,0 +1,56 @@
+# Color frames (ImgFrame), object detection (ImgDetections) and recognition (NNData)
+# messages arrive to the host all with some additional delay.
+# For each ImgFrame there's one ImgDetections msg, which has multiple detections, and for each
+# detection there's a NNData msg which contains recognition results.
+
+# How it works:
+# Every ImgFrame, ImgDetections and NNData message has it's own sequence number, by which we can sync messages.
+
+class TwoStageHostSeqSync:
+ def __init__(self):
+ self.msgs = {}
+ # name: color, detection, or recognition
+ def add_msg(self, msg, name):
+ seq = str(msg.getSequenceNum())
+ if seq not in self.msgs:
+ self.msgs[seq] = {} # Create directory for msgs
+ if "recognition" not in self.msgs[seq]:
+ self.msgs[seq]["recognition"] = [] # Create recognition array
+
+ if name == "recognition":
+ # Append recognition msgs to an array
+ self.msgs[seq]["recognition"].append(msg)
+ # print(f'Added recognition seq {seq}, total len {len(self.msgs[seq]["recognition"])}')
+
+ elif name == "detection":
+ # Save detection msg in the directory
+ self.msgs[seq][name] = msg
+ self.msgs[seq]["len"] = len(msg.detections)
+ # print(f'Added detection seq {seq}')
+
+ elif name == "color": # color
+ # Save color frame in the directory
+ self.msgs[seq][name] = msg
+ # print(f'Added frame seq {seq}')
+
+
+ def get_msgs(self):
+ seq_remove = [] # Arr of sequence numbers to get deleted
+
+ for seq, msgs in self.msgs.items():
+ seq_remove.append(seq) # Will get removed from dict if we find synced msgs pair
+
+ # Check if we have both detections and color frame with this sequence number
+ if "color" in msgs and "len" in msgs:
+
+ # Check if all detected objects (faces) have finished recognition inference
+ if msgs["len"] == len(msgs["recognition"]):
+ # print(f"Synced msgs with sequence number {seq}", msgs)
+
+ # We have synced msgs, remove previous msgs (memory cleaning)
+ for rm in seq_remove:
+ del self.msgs[rm]
+
+ return msgs # Returned synced msgs
+
+ return None # No synced msgs
\ No newline at end of file
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/README.md b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/README.md
new file mode 100644
index 0000000000..45602db429
--- /dev/null
+++ b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/README.md
@@ -0,0 +1,92 @@
+[中文文档](README.zh-CN.md)
+
+Face recognition
+================
+
+This example demonstrates the DepthAI running [face detection network](https://docs.openvinotoolkit.org/2021.3/omz_models_model_face_detection_retail_0004.html), [head posture estimation network](https://docs.openvinotoolkit.org/2021.3/omz_models_model_head_pose_estimation_adas_0001.html) and [face recognition network](https://docs.openvinotoolkit.org/2021.3/omz_models_model_face_recognition_mobilefacenet_arcface.html). It recognizes multiple faces at once on the frame.
+
+## Demo
+
+[](https://youtu.be/Xb1cXu_SIbo)
+
+
+### How it works
+
+1. The color camera produces high-res frames, sends them to host, Script node, and downscale ImageManip node.
+2. Downscale ImageManip will downscale from high-res frame to `300x300`, required by 1st NN in this pipeline; object detection model.
+3. `300x300` frames are sent from downscale ImageManip node to the object detection model (MobileNetDetectionNetwork).
+4. Object detections are sent to the Script node.
+5. Script node first syncs object detections msg with frame. It then goes through all detections and creates ImageManipConfig for each detected face. These configs then get sent to ImageManip together with synced high-res frame.
+6. ImageManip will crop only the face out of the original frame. It will also resize the face frame to the required size (`60x60`) by the head pose estimation NN model.
+7. Face frames get sent to the 2nd NN - head pose estimation NN model. NN estimations results are sent back to the Script node together with the passthrough frame (for syncing).
+8. Script node syncs the head pose estimation, high-res frame, and face detection results. It then creates ImageManipConfig that will rotate the bounding box so that the face will also be vertical, which significantly improves face recognition accuracy.
+9. Created ImageManipConfig and high-res frame get sent to another ImageManip node, which crops rotated rectangle and feeds the `112x112` frame to the 3rd NN: face recognition the arcface model.
+10. Frames, object detections, and recognition results are all **synced on the host** side.
+11. Face recognition results are matched with faces in the database using cosine distance (inside `FaceRecognition` class) and then displayed to the user.
+
+## Pipeline graph
+
+
+
+[DepthAI Pipeline Graph](https://github.com/geaxgx/depthai_pipeline_graph#depthai-pipeline-graph-experimental) was used to generate this image.
+
+
+## Potential improvements
+
+This is a demo app showcasing the capability of depthai, not a production-ready solution. A few potential improvements:
+
+- Use scalable DB system for comparing face embeddings. If you have a large DB (eg. thousands of people), this will be extremely important, as comparing numpy arrays in python `for` loop isn't very fast/efficient.
+- Trying a different face recognition model that could potentially be more accurate. A few other pretrained models: [Sphereface](https://docs.openvino.ai/2021.4/omz_models_model_Sphereface.html), [face_recognition_resnet100_arcface_onnx](https://docs.openvino.ai/2021.4/omz_models_model_face_recognition_resnet100_arcface_onnx.html), [face_reidentification_retail_0095](https://docs.openvino.ai/2021.4/omz_models_model_face_reidentification_retail_0095.html), [facenet-20180408-102900](https://docs.openvino.ai/2021.4/omz_models_model_facenet_20180408_102900.html).
+- Use a different (potentially more accurate) comparison technique to compare face embeddings. We currently use cosine distance.
+- Use depth as well to increase face recognition accuracy and for anti-spoofing. We haven't found any opensource/pretrained NN models that would accept color+depth frames for better accuracy, but Varun from LearnOpenCv used depth in the [anti-spoofing face recognition system](https://learnopencv.com/anti-spoofing-face-recognition-system-using-oak-d-and-depthai/) to determine whether the image is flat (2D image, eg. from a phone).
+- Use anti-spoofing model, an example would be [Silent-Face-Anti-Spoofing](https://github.com/minivision-ai/Silent-Face-Anti-Spoofing) repo. Another option to check whether it's a face photo would be to check for blinks, [demo here](https://github.com/luxonis/depthai-experiments/tree/master/gen2-fatigue-detection#gen2-fatigue-detection).
+
+## Usage
+
+```bash
+usage: main.py [-name NAME]
+
+optional arguments:
+ -name, --name Name of the person for database saving [Optional]
+
+```
+
+**Before this example works, you have to "teach" it what face to associate with which name:**
+
+1. Run `python3 main.py --name JohnDoe`. Then you should face the camera to JohnDoe from different angles, so he will later be recognized from different angles as well. This will just save (his) face vectors to the person's databse (in this case `JohnDoe.npz`).
+2. Repeat step 1 for other people you would like to recognize
+3. Run `python3 main.py` for face recognition demo. Whenever the device sees a new face, it will calculate the face vector (arcface NN model) and it will get compared with other vectors from the databases (`.npz`) using cosine distance.
+
+
+## How it works:
+
+### 1. Run the face detection model
+
+> Run the [face-detection-retail-0004](models/face-detection-retail-0004_openvino_2020_1_4shave.blob) model to
+> detect the face in the image and intercept the facial image.
+>
+> 
+
+### 2. Run head-pose-estimation model
+
+> Run the [head-pose-estimation-adas-0001](models/head-pose-estimation-adas-0001.blob) model to
+> Detect head tilt angle and adjust head posture.
+>
+>
+
+### 3. Run face recognition model
+
+> Run the [face-recognition-mobilefacenet-arcface.blob](models/face-recognition-mobilefacenet-arcface_2021.2_4shave.blob) model to
+> Recognize the face.
+>
+> 
+
+--------------------
+
+## Pre-requisites
+
+Install requirements
+
+```bash
+python3 -m pip install -r requirements.txt
+```
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/README.zh-CN.md b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/README.zh-CN.md
new file mode 100644
index 0000000000..2ee0d31161
--- /dev/null
+++ b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/README.zh-CN.md
@@ -0,0 +1,79 @@
+[英文文档](README.md)
+
+人脸检测
+================
+
+该示例演示了Gen2 Pipeline Builder运行的人[face detection network](https://docs.openvinotoolkit.org/latest/omz_models_model_face_detection_retail_0004.html) ,[head posture estimation network](https://docs.openvinotoolkit.org/latest/omz_models_model_head_pose_estimation_adas_0001.html) 和 [face recognition network](https://docs.openvinotoolkit.org/latest/omz_models_model_face_recognition_mobilefacenet_arcface.html)
+
+
+[](https://www.youtube.com/watch?v=HNAeBwNCRek "Face recognition")
+
+## 这个怎么运作:
+
+### 1. 运行人脸检测模型
+
+> 运行 [face-detection-retail-0004](models/face-detection-retail-0004_openvino_2020_1_4shave.blob) 模型以检测图像中的面部并截取面部图像。
+>
+> 
+
+### 2. 运行头姿势估计模型
+
+> 运行 [head-pose-estimation-adas-0001](models/head-pose-estimation-adas-0001.blob)模型以检测头部倾斜角度并调整头部姿势。
+>
+>
+
+### 3. 跑步人脸识别模型
+
+> 运行 [face-recognition-mobilefacenet-arcface.blob](models/face-recognition-mobilefacenet-arcface_2021.2_4shave.blob) 模型以识别人脸。
+>
+> 
+
+--------------------
+
+## 先决条件
+
+1.购买DepthAI模型(请参见 [shop.luxonis.com](https://shop.luxonis.com/))
+2. 安装依赖
+ ```bash
+ python3 -m pip install -r requirements.txt
+ ```
+
+
+## 用法
+
+```bash
+用法: main.py [-h] [-nd] [-cam] [-vid VIDEO]
+
+可选参数:
+ -h, --help 显示此帮助消息并退出
+ -nd, --no-debug 阻止调试输出
+ -cam, --camera 使用DepthAI 4K RGB相机进行推理(与-vid冲突)
+ -vid VIDEO, --video VIDEO
+ 用于推理的视频文件的路径(与-cam冲突)
+ -db, --databases 保存数据(仅在运行识别网络时使用)
+ -n NAME, --name NAME NAME数据名称(与-db一起使用)[可选]
+
+```
+
+与视频文件一起使用并建立人脸数据库
+```bash
+python main.py -db -n -vid
+```
+
+要与DepthAI 4K RGB相机一起使用,请改用
+```bash
+python main.py -db -n -cam
+```
+
+要与视频文件一起使用,请使用以下参数运行脚本
+
+```bash
+python main.py -vid
+```
+
+要与DepthAI 4K RGB相机一起使用,请改用
+```bash
+python main.py -cam
+```
+
+> 按“ q”退出程序。
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/__init__.py b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/databases/np b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/databases/np
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/databases/plt b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/databases/plt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/detection_face.png b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/detection_face.png
new file mode 100644
index 0000000000..db19dfd2b6
Binary files /dev/null and b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/detection_face.png differ
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/face_corr.png b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/face_corr.png
new file mode 100644
index 0000000000..a4454ce701
Binary files /dev/null and b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/face_corr.png differ
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/face_reg.png b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/face_reg.png
new file mode 100644
index 0000000000..fa8548e731
Binary files /dev/null and b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/images/face_reg.png differ
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/requirements.txt b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/requirements.txt
new file mode 100644
index 0000000000..dc2823cabc
--- /dev/null
+++ b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/requirements.txt
@@ -0,0 +1,4 @@
+depthai==2.20.2.0
+blobconverter==1.3.0
+opencv-python
+numpy
\ No newline at end of file
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/script.py b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/script.py
new file mode 100644
index 0000000000..63f209c767
--- /dev/null
+++ b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/script.py
@@ -0,0 +1,92 @@
+import time
+sync = {} # Dict of messages
+
+# So the correct frame will be the first in the list
+# For this experiment this function is redundant, since everything
+# runs in blocking mode, so no frames will get lost
+def get_sync(target_seq):
+ seq_remove = [] # Arr of sequence numbers to get deleted
+ for seq, msgs in sync.items():
+ if seq == str(target_seq):
+ # We have synced msgs, remove previous msgs (memory cleaning)
+ for rm in seq_remove:
+ del sync[rm]
+ return msgs
+ seq_remove.append(seq) # Will get removed from dict if we find synced sync pair
+ return None
+def find_frame(target_seq):
+ if str(target_seq) in sync and "frame" in sync[str(target_seq)]:
+ return sync[str(target_seq)]["frame"]
+def add_detections(det, seq):
+ # No detections, we can remove saved frame
+ if len(det) == 0:
+ del sync[str(seq)]
+ else:
+ # Save detections, as we will need them for face recognition model
+ sync[str(seq)]["detections"] = det
+
+def correct_bb(bb):
+ if bb.xmin < 0: bb.xmin = 0.001
+ if bb.ymin < 0: bb.ymin = 0.001
+ if bb.xmax > 1: bb.xmax = 0.999
+ if bb.ymax > 1: bb.ymax = 0.999
+
+while True:
+ time.sleep(0.001)
+ preview = node.io['preview'].tryGet()
+ if preview is not None:
+ sync[str(preview.getSequenceNum())] = {}
+ sync[str(preview.getSequenceNum())]["frame"] = preview
+
+ face_dets = node.io['face_det_in'].tryGet()
+ if face_dets is not None:
+ # node.warn(f"New detection start")
+ passthrough = node.io['face_pass'].get()
+ seq = passthrough.getSequenceNum()
+ # node.warn(f"New detection {seq}")
+ if len(sync) == 0: continue
+ img = find_frame(seq) # Matching frame is the first in the list
+ if img is None: continue
+
+ add_detections(face_dets.detections, seq)
+
+ for det in face_dets.detections:
+ cfg = ImageManipConfig()
+ correct_bb(det)
+ cfg.setCropRect(det.xmin, det.ymin, det.xmax, det.ymax)
+ cfg.setResize(60, 60)
+ cfg.setKeepAspectRatio(False)
+ node.io['manip_cfg'].send(cfg)
+ node.io['manip_img'].send(img)
+
+ headpose = node.io['headpose_in'].tryGet()
+ if headpose is not None:
+ # node.warn(f"New headpose")
+ passthrough = node.io['headpose_pass'].get()
+ seq = passthrough.getSequenceNum()
+ # node.warn(f"New headpose seq {seq}")
+ # Face rotation in degrees
+ r = headpose.getLayerFp16('angle_r_fc')[0] # Only 1 float in there
+
+ msgs = get_sync(seq)
+ bb = msgs["detections"].pop(0)
+ correct_bb(bb)
+
+ # remove_prev_frame(seq)
+ img = msgs["frame"]
+ # node.warn('HP' + str(img))
+ # node.warn('bb' + str(bb))
+ cfg = ImageManipConfig()
+ rr = RotatedRect()
+ rr.center.x = (bb.xmin + bb.xmax) / 2
+ rr.center.y = (bb.ymin + bb.ymax) / 2
+ rr.size.width = bb.xmax - bb.xmin
+ rr.size.height = bb.ymax - bb.ymin
+ rr.angle = r # Rotate the rect in opposite direction
+ # True = coordinates are normalized (0..1)
+ cfg.setCropRotatedRect(rr, True)
+ cfg.setResize(112, 112)
+ cfg.setKeepAspectRatio(True)
+
+ node.io['manip2_cfg'].send(cfg)
+ node.io['manip2_img'].send(img)
\ No newline at end of file
diff --git a/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/with dai.py b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/with dai.py
new file mode 100644
index 0000000000..c089cb04c1
--- /dev/null
+++ b/src/main/resources/resource/Py4j/OakD/gen2_face_recognition/with dai.py
@@ -0,0 +1,27 @@
+with dai.Device(pipeline) as device:
+
+ # Output queues will be used to get the rgb frames and nn data from the outputs defined above
+ qRgb = device.getOutputQueue(name="rgb", maxSize=4, blocking=False)
+ qDet = device.getOutputQueue(name="nn", maxSize=4, blocking=False)
+
+ frame = None
+ detections = []
+ startTime = time.monotonic()
+ counter = 0
+ color2 = (255, 255, 255)
+
+ # nn data, being the bounding box locations, are in <0..1> range - they need to be normalized with frame width/height
+ def frameNorm(frame, bbox):
+ normVals = np.full(len(bbox), frame.shape[0])
+ normVals[::2] = frame.shape[1]
+ return (np.clip(np.array(bbox), 0, 1) * normVals).astype(int)
+
+ def displayFrame(name, frame):
+ color = (255, 0, 0)
+ for detection in detections:
+ bbox = frameNorm(frame, (detection.xmin, detection.ymin, detection.xmax, detection.ymax))
+ cv2.putText(frame, labelMap[detection.label], (bbox[0] + 10, bbox[1] + 20), cv2.FONT_HERSHEY_TRIPLEX, 0.5, 255)
+ cv2.putText(frame, f"{int(detection.confidence * 100)}%", (bbox[0] + 10, bbox[1] + 40), cv2.FONT_HERSHEY_TRIPLEX, 0.5, 255)
+ cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2)
+ # Show the frame
+ cv2.imshow(name, frame)
diff --git a/src/main/resources/resource/Py4j/Py4j.py b/src/main/resources/resource/Py4j/Py4j.py
index 3c75cd94c5..89c37c1189 100644
--- a/src/main/resources/resource/Py4j/Py4j.py
+++ b/src/main/resources/resource/Py4j/Py4j.py
@@ -1,22 +1,47 @@
################################
# Py4j.py
+# Py4j is a Gateway
+# Just like the WebGui is a Gateway
+# Py4j should have a convention of remote services like {ServiceName}Handler - as all its gateway services are in the jvm
+#
# more info here: https://www.py4j.org/
-# Py4J enables Python programs running in a Python interpreter to dynamically access
-# Java objects in a Java Virtual Machine.
-# Methods are called as if the Java objects resided in the Python interpreter and
-# Java collections can be accessed through standard Python collection methods.
+# Py4J enables Python programs running in a Python interpreter to dynamically access
+# Java objects in a Java Virtual Machine.
+# Methods are called as if the Java objects resided in the Python interpreter and
+# Java collections can be accessed through standard Python collection methods.
# Py4J also enables Java programs to call back Python objects. Py4J is distributed under the BSD license
# Python 2.7 -to- 3.x is supported
# In your python 3.x project
# pip install py4j
# you have full access to mrl instance that's running
# the gateway
+# Handlers represent proxied processing of messages - same as {service}Gui in the WebGui
+
+# with open('src/main/resources/resource/Py4j/Py4j.py', 'r') as file:
+# script_code = file.read()
+
+# exec(script_code)
+
-import sys
import json
+import sys
from abc import ABC, abstractmethod
-from py4j.java_collections import JavaObject, JavaClass
-from py4j.java_gateway import JavaGateway, CallbackServerParameters, GatewayParameters
+
+from py4j.java_collections import JavaClass, JavaObject
+from py4j.java_gateway import CallbackServerParameters, GatewayParameters, JavaGateway
+
+# REQUIRED TO SET FIELDS !
+# from py4j.java_gateway import set_field
+# rename java_object proxy or service?
+
+
+class SingletonMeta(type):
+ _instances = {}
+
+ def __call__(cls, *args, **kwargs):
+ if cls not in cls._instances:
+ cls._instances[cls] = super().__call__(*args, **kwargs)
+ return cls._instances[cls]
class Service(ABC):
@@ -30,11 +55,13 @@ def __getattr__(self, attr):
def __str__(self):
# Delegate string representation to the underlying Java object
return str(self.java_object)
-
- def subscribe(self, event):
+
+ def subscribe(self, topic, method):
print("subscribe")
- self.java_object.subscribe(event)
-
+ self.java_object.subscribe(topic, method)
+
+ # FIXME - TODO - getName(), getConfig(), send(), invoke(), sendBlocking()
+
@abstractmethod
def getType(self):
pass
@@ -42,8 +69,8 @@ def getType(self):
class NeoPixel(Service):
def __init__(self, name):
- super().__init__(name)
-
+ super().__init__(name)
+
def getType(self):
return "NeoPixel"
@@ -51,22 +78,6 @@ def onFlash(self):
print("onFlash")
-class InMoov2(Service):
- def __init__(self, name):
- super().__init__(name)
- self.subscribe('onStateChange')
-
- def getType(self):
- return "InMoov2"
-
- def onOnStateChange(self, state):
- print("onOnStateChange")
- print(state)
- print(state.get('last'))
- print(state.get('current'))
- print(state.get('event'))
-
-
# TODO dynamically add classes that you don't bother to check in
# class Runtime(Service):
@@ -77,7 +88,8 @@ def onOnStateChange(self, state):
# FIXME - REMOVE THIS - DO NOT SET ANY GLOBALS !!!!
runtime = None
-# TODO - rename to mrl_lib ?
+
+# TODO - rename to mrl_lib ?
# e.g.
# mrl = mrl_lib.connect("localhost", 1099)
# i01 = InMoov("i01", mrl)
@@ -85,7 +97,10 @@ def onOnStateChange(self, state):
# runtime = mrl_lib.connect("localhost", 1099) # JVM connection Py4j instance needed for a gateway
# runtime.start("i01", "InMoov2") # starts Java service
# runtime.start("nativePythonService", "NativePythonClass") # starts Python service no gateway needed
-class MessageHandler(object):
+# class MessageHandler(object):
+
+
+class MessageHandler(metaclass=SingletonMeta):
"""
The class responsible for receiving and processing Py4j messages,
including handling `invoke()` and `exec()` requests. Class
@@ -95,54 +110,67 @@ class MessageHandler(object):
def __init__(self):
global runtime
- # initializing stdout and stderr
- print("initializing")
- self.name = None
- self.stdout = sys.stdout
- self.stderr = sys.stderr
- sys.stdout = self
- sys.stderr = self
- self.gateway = JavaGateway(callback_server_parameters=CallbackServerParameters(),
- python_server_entry_point=self,
- gateway_parameters=GatewayParameters(auto_convert=True))
- self.runtime = self.gateway.jvm.org.myrobotlab.service.Runtime.getInstance()
- # FIXME - REMOVE THIS - DO NOT SET ANY GLOBALS !!!!
- runtime = self.runtime
- self.py4j = None # need to wait until name is set
- print("initialized ... waiting for name to be set")
+ try:
+ # initializing stdout and stderr
+ print("initializing")
+ self.name = None
+ self.stdout = sys.stdout
+ self.stderr = sys.stderr
+ # registry is registry of py4j services
+ self.registry = {}
+ # registry_handlers is registry of python handlers for java proxies
+ self.registry_handlers = {}
+ sys.stdout = self
+ sys.stderr = self
+ # print("creating gateway")
+ self.gateway = JavaGateway(
+ callback_server_parameters=CallbackServerParameters(),
+ python_server_entry_point=self,
+ gateway_parameters=GatewayParameters(
+ auto_convert=True, auto_field=True
+ ),
+ )
+ self.runtime = self.gateway.jvm.org.myrobotlab.service.Runtime.getInstance()
+ # FIXME - REMOVE THIS - DO NOT SET ANY GLOBALS !!!!
+ runtime = self.runtime
+ self.py4j = None # need to wait until name is set
+ print("initialized ... waiting for name to be set")
+ except Exception as e:
+ print(e)
def construct_runtime(self):
"""
+ FIXME - remove this method
Constructs a new Runtime instance and returns it.
"""
jvm_runtime = self.gateway.jvm.org.myrobotlab.service.Runtime.getInstance()
-
+
# Define class attributes and methods as dictionaries
class_attributes = {
- 'x': 0,
- 'y': 0,
- 'move': lambda self, dx, dy: setattr(self, 'x', self.x + dx) or setattr(self, 'y', self.y + dy),
- 'get_position': lambda self: (self.x, self.y),
+ "x": 0,
+ "y": 0,
+ "move": lambda self, dx, dy: setattr(self, "x", self.x + dx)
+ or setattr(self, "y", self.y + dy),
+ "get_position": lambda self: (self.x, self.y),
}
# Create the class dynamically using the type() function
- MyDynamicClass = type('MyDynamicClass', (object,), class_attributes)
+ MyDynamicClass = type("MyDynamicClass", (object,), class_attributes)
# Create an instance of the dynamically created class
obj = MyDynamicClass()
-
return self.runtime
# Define the callback function
def handle_connection_break(self):
# Add your custom logic here to handle the connection break
- print("Connection with Java gateway was lost or terminated.")
+ print("Connection with Java gateway was lost or terminated.")
print("goodbye.")
sys.exit(1)
- def write(self,string):
- if (self.py4j):
+ def write(self, string):
+ if self.py4j:
self.py4j.handleStdOut(string)
def flush(self):
@@ -164,10 +192,29 @@ def setName(self, name):
print("python started", sys.version)
print("runtime attached", self.runtime.getVersion())
- print("reference to runtime")
+ print("services", self.runtime.getServiceNames())
+ for name in self.runtime.getServiceNames():
+ print(name)
+ self.registry[name] = self.runtime.getService(name)
+ long_type = self.registry.get(name).getTypeKey()
+ parts = long_type.split(".")
+ if len(parts) > 1:
+ short_type = parts[-1]
+ else:
+ short_type = long_type
+
+ print("simpleName", self.registry.get(name).getTypeKey())
+
+ # , 'InMoov2': }
+ # and isinstance(globals()[short_type], type): - module needs to be __main__
+ if short_type in globals():
+ print(f"'{short_type}' exists as a class in the global space.")
+ else:
+ print(f"'{short_type}' will be unknown.")
+
# TODO print env vars PYTHONPATH etc
return name
-
+
def getRuntime(self):
return self.runtime
@@ -261,11 +308,424 @@ def convert_array(self, array):
return result
class Java:
- implements = ['org.myrobotlab.framework.interfaces.Invoker']
+ implements = ["org.myrobotlab.framework.interfaces.Invoker"]
+
+
+############################################################################################################
+# service specific
-handler = MessageHandler()
-if len(sys.argv) > 1:
- handler.setName(sys.argv[1])
-else:
- raise RuntimeError("This script requires the full name of the Py4j service as its first command-line argument")
+# IMPORTANT - Jython could call overloaded methods, Py4j cannot !!!
+# names are guaranteed to be unique
+# the kluge of changing period to underscore
+# auto create globals ? (bad idea)
+# isolate to callbacks
+# from py4j.java_gateway import JavaObject
+
+# unsuccessful attempt to modularize
+# class InMoov2(JavaObject):
+# global runtime
+
+# def __init__(self, name):
+# # super().__init__(runtime.start(name, "InMoov2"))
+# super().__init__("blah", handler.gateway)
+# self.name = name
+# self.robot = runtime.start(name, "InMoov2")
+
+
+# FOR SUBSCRIPTIONS WHOS DESTINATION IS PYTHON
+# SHOULD PROBABLY BE MAINTAINED HERE
+
+
+# global routing - handle inside of py4j router
+def onPythonMessage(msg):
+ # FIXME - don't know hot to fix yet
+ global python_runtime
+ """Initial processing point for all messages.
+ Messages are sent from the Java side to the Python side,
+ then decoded and routed to the appropriate method.
+
+ Decoding msg is unwrapped from the tunnelled message, and
+ the python method is called with a parameter of the tunnelled.sender
+ e.g.
+ onCallback('i01', data):
+ # msg body
+
+ The preferred way would be to use a python side service class where self is the
+ first parameter, and the service is returned from the registry.
+
+ e.g.
+ onCallback(self, data):
+ # msg body
+
+ But I have not figured out how to augment py4j to do this... yet.
+
+ Args:
+ msg (_type_): tunnelled message, with data message
+ """
+ try:
+ print("onPythonMessage method", msg.get("method"))
+ print("onPythonMessage data", msg.get("data"))
+ print("onPythonMessage sender", msg.get("sender"))
+ # untunnelled msg
+ # untunnelled = msg.get[0]("data")
+ # print("untunnelled", untunnelled.get("method"), untunnelled.get("data"))
+ # exec(msg.method)(msg)
+ # exec(code, globals())
+ # registry.get(msg.sender)
+ except Exception as e:
+ print(e)
+
+
+class InMoov2(Service):
+ def __init__(self, name):
+ super().__init__(name)
+ self.subscribe(name, "onStateChange")
+
+ def getType(self):
+ return "InMoov2"
+
+ def onOnStateChange(self, state):
+ print("onOnStateChange")
+ print(state)
+ print(state.get("last"))
+ print(state.get("current"))
+ print(state.get("event"))
+
+ # FIXME - global method not name specific
+ def onStartSpeaking(self, text):
+ global runtime
+
+ print("onStartSpeaking", text)
+
+ # FIXME FIXME FIXME name delivered as a parameter or
+ # make class that is created by the runtime with name
+ i01 = runtime.getService("i01")
+
+ if i01:
+ # i01_neoPixel = i01.getPeer("neoPixel")
+ # if i01_neoPixel and i01.getConfig().neoPixelFlashWhenSpeaking:
+ # i01_neoPixel.setAnimation("Ironman", 255, 255, 255, 20)
+
+ # Great idea, but has hardcoded translations - this should be done in the language service but the
+ # raw gesture still be processed
+ # if 'oui ' in text or 'yes ' in text or ' oui' in text or 'ja ' in text or text=="yes" or text=="kyllä":Yes()
+ # if 'non ' in text or 'no ' in text or 'nicht ' in text or 'neen ' in text or text=="no" or text=="ei":No()
+
+ # force random move while speaking, to avoid conflict with random life gesture
+ if i01.getConfig().robotCanMoveHeadWhileSpeaking:
+ i01_random = runtime.getService("i01.random")
+ if i01_random and i01.getState() != "tracking":
+ i01_random.disableAll()
+ i01_random.enable("i01.setHeadSpeed")
+ i01_random.enable("i01.moveHead")
+ i01_random.enable()
+
+ # FIXME - global method not name specific
+ def onEndSpeaking(self, text):
+ global runtime
+ print("onEndSpeaking", text)
+ i01 = runtime.getService("i01")
+ i01_random = runtime.getService("i01.random")
+ if i01_random and i01.getState() != "tracking":
+ # i01_random.disable("i01.setHeadSpeed")
+ # i01_random.disable("i01.moveHead")
+ i01_random.disable()
+
+ # if i01:
+ # if i01.getConfig().robotCanMoveHeadWhileSpeaking:
+ # i01_random = runtime.getService("i01.random")
+ # if i01_random:
+ # i01_random.disable()
+ # i01_random.enable('i01.moveLeftArm')
+ # i01_random.enable('i01.moveRightArm')
+ # i01_random.enable('i01.moveLeftHand')
+ # i01_random.enable('i01.moveRightHand')
+ # i01_random.enable('i01.moveTorso')
+ # i01_random.enable('i01.setLeftArmSpeed')
+ # i01_random.enable('i01.setRightArmSpeed')
+ # i01_random.enable('i01.setLeftHandSpeed')
+ # i01_random.enable('i01.setRightHandSpeed')
+ # i01_random.enable('i01.setTorsoSpeed')
+ # if runtime.isStarted('i01.head'):
+ # i01_random.addRandom(200, 1000, "i01", "setHeadSpeed", 8.0, 20.0, 8.0, 20.0, 8.0, 20.0)
+ # i01_random.addRandom(200, 1000, "i01", "moveHead", 70.0, 110.0, 65.0, 115.0, 70.0, 110.0)
+ # i01_neoPixel = runtime.getService('i01.neoPixel')
+ # if i01.getConfig().neoPixelFlashWhenSpeaking and runtime.isStarted("i01.neoPixel"):
+ # i01_neoPixel.clear()
+
+ # Sensor events begin ========================================
+
+ def on_pir_on(self, name):
+ robot = runtime.getService(name)
+ # FIXME - chatBot.getResponse("SYSTEM_EVENT on_start")
+ mouth = robot.getPeer("mouth")
+ mouth.speak("I feel your presence")
+ print("on_pir_on", name)
+
+ def on_pir_off(self, name):
+ robot = runtime.getService(name)
+ # FIXME - chatBot.getResponse("SYSTEM_EVENT on_start")
+ mouth = robot.getPeer("mouth")
+ mouth.speak("I'm so alone")
+ print("on_pir_off", name)
+
+ # Sensor events end ==========================================
+ # Topic events begin ========================================
+ # FIXME - since its a direct subscribe from i01.chatBot -to-> python
+ # we don't have a name - so we can't use the name as a parameter
+
+ def onTopic(self, topic_event):
+ """Called when topic changes in chatbot,
+ is rebroadcasted from InMoov2
+ TopicEvent.
+ botname = name of bot
+ use = name of user
+ topic = current topic
+ src = name of source
+
+ Args:
+ topic (_type_): _description_
+ """
+ # FIXME - find a solution for the hardcoded name !
+ # route through inmoov add name as field
+ robot = runtime.getService(topic_event.src)
+ mouth = robot.getPeer("mouth")
+ if mouth:
+ mouth.speak("New topic, the topic is " + topic_event.topic)
+ print("onTopic", topic_event.topic)
+
+ # Topic events end ==========================================
+ # State change events begin ========================================
+
+ def onStateChange(self, state_event):
+ """The main router for state changes
+ it calls the appropriate method based on the state change
+ Hooked to InMoov2.publishStateChange
+
+ Args:
+ data (InMoov2State): contains src and state
+ """
+
+ # Python 2 unicode pain
+ state = str(state_event.state)
+ src = str(state_event.src)
+
+ robot = runtime.getService(src)
+
+ # leaving state changes
+ fsm = robot.getPeer("fsm")
+ random = robot.getPeer("random")
+ mouth = robot.getPeer("mouth")
+ chatBot = robot.getPeer("chatBot")
+
+ if fsm:
+ leavingState = fsm.getPreviousState()
+
+ if random and leavingState == "random":
+ random.disable()
+
+ if chatBot:
+ chatBot.setPredicate("state", state)
+
+ # if chatBot and leavingState == "first_init":
+ # # move the botname from human predicates to new user predicates
+ # # chatBot.setPredicate("botname", chatBot.getPredicate("human","botname")
+ # # this sets the first user to be the one identified at the end of first_init
+ # chatBot.setConfigValue("username", chatBot.getUsername())
+ # # Not sure if this should only be maintained in predicates
+ # # but config.username is the first session
+ # # it is "modifying" config hower, which might be difficult to support
+ # chatBot.save()
+
+ # FIXME - chatBot.getResponse("SYSTEM_EVENT on_start")
+ if mouth:
+ mouth.speak("leaving " + leavingState + " state and entering " + state)
+
+ print("on_state_change", src, state)
+
+ # call the new state handler
+ eval("on_" + state + "('" + src + "')")
+
+ def on_start(self, name):
+ """Start is where all custom activity can begin.
+ It is the first state changed called after boot.
+ At this point InMoov2 service has started and andy
+ runtime configuration has been processed.
+ The few services required to be running fsm chatBot and python
+ will be running. Other services may need to be checked
+ e.g.
+ opencv = runtime.getService("i01.opencv")
+ if opencv:
+ .... do something
+ Args:
+ name (string): name of service
+ """
+ robot = runtime.getService(name)
+ chatbot = robot.getPeer("chatBot")
+ # FIXME - chatBot.getResponse("SYSTEM_EVENT on_start")
+ mouth = robot.getPeer("mouth")
+ fsm = robot.getPeer("fsm")
+
+ # iterate through all current started peers
+ # and add subscriptions to this service ?
+ # remove all the python subscriptions from InMoov2Config ?
+
+ # FIXME - chatBot.getResponse("SYSTEM_EVENT on_start")
+ if mouth:
+ mouth.speak("I am starting")
+ print("on_start state change from", name)
+ # TODO - make a boot report and give it - errors and warnings ?
+ # TODO - reporting in led display or verbal or wait for verbal
+
+ # human by default is the first user and first predicate file
+ # on startup try to identify the user
+ if chatbot.getUsername() == "human":
+ # try to identify user go through FIRST_INIT
+ fsm.fire("first_init")
+ else:
+ # if user is known - go through WAKE_UP
+ fsm.fire("wake")
+
+ def on_first_init(self, name):
+ """Purpose of this state is to identify the user
+ and first initial configuration of the robot.
+
+ The user should be asked to identify themselves.
+
+ Various other information could be gathered as well
+ although the user should be able to leave at any time.
+
+ Coming back to this state should be possible at any time.
+
+ Args:
+ name (string): name of InMoov2 robot
+ """
+ robot = runtime.getService(name)
+ chatbot = robot.getPeer("chatBot")
+ chatbot.getResponse("NEW_USER")
+
+ # do anything else desired
+ # generate picture data of the user
+ # go through personal questionare
+ # e.g. ask
+
+ def on_wake(self, name):
+ robot = runtime.getService(name)
+ chatbot = robot.getPeer("chatBot")
+ chatbot.getResponse("WAKE_UP")
+ print("on_wake state change from", name)
+
+ def on_idle(self, name):
+ robot = runtime.getService(name)
+ mouth = robot.getPeer("mouth")
+ mouth.speak("I am idle")
+ print("on_idle state change from", name)
+
+ def on_random(self, name):
+ robot = runtime.getService(name)
+ mouth = robot.getPeer("mouth")
+ mouth.speak("I am doing random stuff")
+ print("on_random state change from", name)
+
+ def on_sleep(self, name):
+ robot = runtime.getService(name)
+ mouth = robot.getPeer("mouth")
+ mouth.speak("I am going to sleep")
+ print("on_sleep state change from", name)
+
+ # State change events end ========================================
+ # Service change events begin ========================================
+
+ def on_peer_started(self, name):
+ robot = runtime.getService(name)
+ mouth = robot.getPeer("mouth")
+ mouth.speak("I am starting a new service")
+ print("on_peer_started service change from", name)
+ # add subscriptions for newly started peers
+
+ def on_peer_released(self, name):
+ robot = runtime.getService(name)
+ mouth = robot.getPeer("mouth")
+ mouth.speak("I am releasing a service")
+ print("on_peer_released service change from", name)
+ # remove subscriptions for newly released peers
+
+ # Service change events end ========================================
+
+ def on_sensor_data(self, data):
+ """generalized sensor handler
+
+ Args:
+ data (_type_): _description_
+ """
+ print("on_sensor_data", data)
+
+ def onHeartbeat(self, sender: str):
+ """onHeartbeat a incremental timer used to drive
+ state machines and other time based events.
+ Heartbeats here do not begin until after boot.
+
+ Args:
+ sender (string): the robot's name sending the heartbeat
+ """
+ print("onHeartbeat", sender)
+
+ robot = runtime.getService(sender)
+ neoPixel = robot.getPeer("neoPixel")
+
+ if neoPixel:
+ neoPixel.flash("heartbeat")
+ # if robot.getState() == "first_init":
+ # robot.setRandomIdle()
+
+ def onPredicate(self, predicate_event):
+ robot = runtime.getService(predicate_event.src)
+ robot.info(
+ "predicate " + predicate_event.name + " changed to " + predicate_event.value
+ )
+ # mouth = robot.getPeer("mouth")
+ # if mouth:
+ # mouth.speak("predicate " + predicate_event.name + " changed to " + predicate_event.value)
+
+ def onSession(self, session_event):
+ robot = runtime.getService(session_event.src)
+ mouth = robot.getPeer("mouth")
+ chatBot = robot.getPeer("chatBot")
+ if mouth:
+ mouth.speak("new session with " + session_event.user)
+ # if chatBot:
+ # chatBot.setTopic("new_user")
+
+ def onMessage(self, msg):
+ robot = runtime.getService(msg.sender)
+ print("onMessage.method", msg.method)
+ print("onMessage.data", msg.data)
+ robot.info("onMessage.method " + msg.method)
+ robot.info("onMessage.data " + str(msg.data))
+ # eval(msg.method)(msg.data
+ # auto invoke method
+ # expand parameters ?
+
+ def on_new_user(self, data):
+ print("on_new_user", data)
+ robot = runtime.getService(data.sender)
+ chatBot = robot.getPeer("chatBot")
+
+ # TODO - some function that identifies user and reconciles identities
+ # until then we'll just create a new user
+ chatBot.setUser(data[0]) # name
+ chatBot.setPredicate("botname", chatBot.getPredicate("human", "botname"))
+
+
+# FIXME - better singleton
+if "handler" not in globals():
+ handler = MessageHandler()
+ if len(sys.argv) > 1:
+ handler.setName(sys.argv[1])
+ else:
+ raise RuntimeError(
+ "This script requires the full name of the Py4j service as its first command-line argument"
+ )
+
+print("loaded Py4j.py")
diff --git a/src/main/resources/resource/Py4j/Py4j_min.py b/src/main/resources/resource/Py4j/Py4j_min.py
new file mode 100644
index 0000000000..5a283326e5
--- /dev/null
+++ b/src/main/resources/resource/Py4j/Py4j_min.py
@@ -0,0 +1,11 @@
+from py4j.java_collections import JavaClass, JavaObject
+from py4j.java_gateway import CallbackServerParameters, GatewayParameters, JavaGateway
+
+gateway = JavaGateway(
+ gateway_parameters=GatewayParameters(auto_convert=True, auto_field=True)
+)
+
+runtime = gateway.jvm.org.myrobotlab.service.Runtime.getInstance()
+
+clock = runtime.start("clock", "Clock")
+clock.startClock()
diff --git a/src/main/resources/resource/Py4j/Runtime.py b/src/main/resources/resource/Py4j/Runtime.py
new file mode 100644
index 0000000000..1cd921fe38
--- /dev/null
+++ b/src/main/resources/resource/Py4j/Runtime.py
@@ -0,0 +1,100 @@
+import asyncio
+import importlib
+import json
+from time import sleep
+
+import websockets
+from codec.codec_util import CodecUtil
+from framework.config import Config
+from framework.service import Service
+
+
+class RuntimeConfig(Config):
+ def __init__(self, config):
+ super().__init__(config)
+
+
+class Runtime(Service):
+ __instance = None
+
+ def __init__(self, name, id=None):
+ if Runtime.__instance is not None:
+ return Runtime.__instance
+ if id is None:
+ id = CodecUtil.generate_name()
+ super().__init__(name, id)
+ Runtime.__instance = self
+ self.registry = {}
+ # TODO - override with command line
+ self.id = CodecUtil.generate_name()
+
+ def getConfig(self) -> Config:
+ return RuntimeConfig("Runtime Configuration")
+
+ @staticmethod
+ def getRuntime():
+ if Runtime.__instance is None:
+ Runtime.__instance = Runtime("runtime")
+ return Runtime.__instance
+
+ def register(self, service: Service):
+ self.registry[service.getName()] = service
+
+ def getService(self, name: str) -> Service:
+ return self.registry[name]
+
+ def getServices(self) -> list:
+ return list(self.registry.values())
+
+ def start(self, name: str, type_: str):
+ if name and type_:
+ new_service = self.load_class(name, self.id, type_)
+ # if type_ not in globals():
+ # raise ValueError(f"Class '{type_}' not found.")
+ # service_class = globals()[type_]
+ # if not issubclass(service_class, Service):
+ # raise ValueError(f"'{type_}' is not a subclass of Service.")
+ # new_service = service_class(name)
+ self.registry[name] = new_service
+ print(f"Service '{name}' of type '{type_}' added to the registry.")
+ return new_service
+ else:
+ raise ValueError("Both name and type are required to add a service")
+
+ def load_class(self, name, id, class_name):
+ try:
+ module_name = class_name.lower()
+ module = importlib.import_module(f"service.{module_name}")
+ my_class = getattr(module, class_name)
+ return my_class(name, id) if my_class else None
+ except (ImportError, AttributeError):
+ return None # Return None if class couldn't be loaded
+
+ def connect(self, url: str):
+ asyncio.run(self.receive_messages())
+
+ async def receive_messages(self, url: str):
+ async with websockets.connect(
+ "ws://localhost:8888/api/messages?id=123"
+ ) as websocket:
+ while True:
+ message = await websocket.recv()
+ print(f"recv message {message}")
+ msg = json.loads(message)
+ print(f'msgId {msg.get("msgId")}')
+ print(f'data {msg.get("data")}')
+ data = msg.get("data")
+ print(f"len data {len(data)}")
+ # data = json.loads(message)
+
+
+# Example usage:
+runtime = Runtime.getRuntime()
+print(runtime.getName()) # Output: runtime
+clock = runtime.start("clock1", "Clock")
+print(clock.getName()) # Output: servo1
+clock.start_clock() # Output: 1604391129.031
+# Creating another Runtime instance won't work
+# runtime2 = Runtime("runtimex") # This will raise a RuntimeError
+
+sleep(5)
diff --git a/src/main/resources/resource/Py4j/codec/__init__.py b/src/main/resources/resource/Py4j/codec/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/resources/resource/Py4j/codec/codec_util.py b/src/main/resources/resource/Py4j/codec/codec_util.py
new file mode 100644
index 0000000000..4fa189fc73
--- /dev/null
+++ b/src/main/resources/resource/Py4j/codec/codec_util.py
@@ -0,0 +1,19 @@
+import random
+
+
+class CodecUtil:
+ # put in codec
+ @staticmethod
+ def generate_name():
+ adjectives = [
+ "mechanical",
+ "electronic",
+ "cybernetic",
+ "autonomous",
+ "programmable",
+ ]
+ nouns = ["android", "cyborg", "robo", "droid", "bot"]
+ adj = random.choice(adjectives)
+ noun = random.choice(nouns)
+
+ return f"{adj}-{noun}"
diff --git a/src/main/resources/resource/Py4j/framework/__init__.py b/src/main/resources/resource/Py4j/framework/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/resources/resource/Py4j/framework/config.py b/src/main/resources/resource/Py4j/framework/config.py
new file mode 100644
index 0000000000..6d2eb482da
--- /dev/null
+++ b/src/main/resources/resource/Py4j/framework/config.py
@@ -0,0 +1,3 @@
+class Config:
+ def __init__(self, config):
+ self.config = config
diff --git a/src/main/resources/resource/Py4j/framework/listener.py b/src/main/resources/resource/Py4j/framework/listener.py
new file mode 100644
index 0000000000..4a42e77273
--- /dev/null
+++ b/src/main/resources/resource/Py4j/framework/listener.py
@@ -0,0 +1,5 @@
+class Listener:
+ def __init__(self, topic_method: str, callback_name: str, callback_method: str):
+ self.topic_method = topic_method
+ self.callback_name = callback_name
+ self.callback_method = callback_method
diff --git a/src/main/resources/resource/Py4j/framework/message.py b/src/main/resources/resource/Py4j/framework/message.py
new file mode 100644
index 0000000000..b254c8d461
--- /dev/null
+++ b/src/main/resources/resource/Py4j/framework/message.py
@@ -0,0 +1,8 @@
+class Message:
+ def __init__(self) -> None:
+ self.msgId = "123"
+ self.method = "method"
+ self.encoding = None
+ self.data = []
+ self.sender = "somename"
+ self.sendingMethod = "foo"
diff --git a/src/main/resources/resource/Py4j/framework/service.py b/src/main/resources/resource/Py4j/framework/service.py
new file mode 100644
index 0000000000..0184fd65c9
--- /dev/null
+++ b/src/main/resources/resource/Py4j/framework/service.py
@@ -0,0 +1,45 @@
+import asyncio
+from abc import ABC, abstractmethod
+
+from .config import Config
+from .message import Message
+
+
+class Service(ABC):
+ def __init__(self, name, id):
+ self.name = name
+ self.id = id
+ self.inbox = asyncio.Queue()
+ self.outbox = asyncio.Queue()
+ self.notify_list = {}
+
+ @abstractmethod
+ def getConfig(self) -> Config:
+ pass
+
+ def getName(self):
+ return self.name
+
+ def in_msg(self, msg: Message):
+ self.inbox.put_nowait(msg)
+
+ def out_msg(self, msg: Message):
+ self.outbox.put_nowait(msg)
+
+ def invoke(self, method: str, *args, **kwargs):
+ try:
+ return self.invoke_on(self, method, *args, **kwargs)
+ except AttributeError:
+ raise ValueError(f"Method '{method}' not found.")
+
+ def invoke_on(self, method: str, *args, **kwargs):
+ try:
+ obj = getattr(self, method)(*args, **kwargs)
+ # TODO - add obj to outbox
+ if method in self.notify_list:
+ for listener in self.notify_list[method]:
+ # TODO - add obj to listener.inbo
+ pass
+ return obj
+ except AttributeError:
+ raise ValueError(f"Method '{method}' not found.")
diff --git a/src/main/resources/resource/Py4j/service/__init__.py b/src/main/resources/resource/Py4j/service/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/resources/resource/Py4j/service/clock.py b/src/main/resources/resource/Py4j/service/clock.py
new file mode 100644
index 0000000000..cb23059616
--- /dev/null
+++ b/src/main/resources/resource/Py4j/service/clock.py
@@ -0,0 +1,34 @@
+import threading
+import time
+
+from framework.config import Config
+from framework.service import Service
+
+
+class Clock(Service):
+ def __init__(self, name, id):
+ super().__init__(name, id)
+ self.is_running = False
+ self.timer = None
+
+ def start_clock(self):
+ if not self.is_running:
+ self.is_running = True
+ self.pulse()
+
+ def pulse(self):
+ if self.is_running:
+ self.publish_time()
+ self.timer = threading.Timer(1, self.pulse)
+ self.timer.start()
+
+ def stop_clock(self):
+ self.is_running = False
+ if self.timer:
+ self.timer.cancel()
+
+ def publish_time(self):
+ print(time.time())
+
+ def getConfig(self) -> Config:
+ return None
diff --git a/src/main/resources/resource/Py4j/service/servo.py b/src/main/resources/resource/Py4j/service/servo.py
new file mode 100644
index 0000000000..2e4b66d888
--- /dev/null
+++ b/src/main/resources/resource/Py4j/service/servo.py
@@ -0,0 +1,10 @@
+from framework.config import Config
+from framework.service import Service
+
+
+class Servo(Service):
+ def __init__(self, name, id):
+ super().__init__(name, id)
+
+ def getConfig(self) -> Config:
+ return None
diff --git a/src/main/resources/resource/RemoteAdapter/connect.png b/src/main/resources/resource/RemoteAdapter/connect.png
deleted file mode 100644
index c8692d8a39..0000000000
Binary files a/src/main/resources/resource/RemoteAdapter/connect.png and /dev/null differ
diff --git a/src/main/resources/resource/Shoutbox.png b/src/main/resources/resource/Shoutbox.png
deleted file mode 100644
index 21a5c47dee..0000000000
Binary files a/src/main/resources/resource/Shoutbox.png and /dev/null differ
diff --git a/src/main/resources/resource/Shoutbox/Shoutbox.py b/src/main/resources/resource/Shoutbox/Shoutbox.py
deleted file mode 100644
index 6fd8b588fb..0000000000
--- a/src/main/resources/resource/Shoutbox/Shoutbox.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# start the service
-shoutbox = runtime.start("shoutbox","Shoutbox")
\ No newline at end of file
diff --git a/src/main/resources/resource/Shoutbox/shoutbox.yml b/src/main/resources/resource/Shoutbox/shoutbox.yml
deleted file mode 100644
index a78690fbbf..0000000000
--- a/src/main/resources/resource/Shoutbox/shoutbox.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-!!org.myrobotlab.service.config.ServiceConfig
-listeners: null
-peers: null
-type: Shoutbox
diff --git a/src/main/resources/resource/Shoutbox/site.htm b/src/main/resources/resource/Shoutbox/site.htm
deleted file mode 100644
index 9fb26e2a0d..0000000000
--- a/src/main/resources/resource/Shoutbox/site.htm
+++ /dev/null
@@ -1,221 +0,0 @@
-
-
-
-