ack.Acknowledgement acknowledgement = 1 {
  """Reply messages for actions that have no response data. Code `OK` means a positive acknowledgement, other codes
negative acknowledgements. A negative acknowledgement may be sent as the reply to any message, even if another reply
is expected. Negative acknowledgements may have a `message` describing the error condition."""

  AcknowledgementCode code = 1;
  string message = 2;

  <= ack.Acknowledgement(code, [message])
    """ack""";
}

version.VersionMessage version_message = 26 {
  """Request or reply for version information of the Hedgehog hardware, firmware, and server software."""

  bytes uc_id = 1;
  string hardware_version = 2;
  string firmware_version = 3;
  string server_version = 4;

  => version.Request()
    """version request => version reply""";
  <= version.Reply(uc_id, hardware_version, firmware_version, server_version)
    """version reply""";
}

emergency.EmergencyAction emergency_action = 27 {
  """Activates or releases the emergency-stop of the HWC."""

  bool activate = 1;

  => emergency.Action(activate)
    """emergency action => ack""";
}

emergency.EmergencyMessage emergency_message = 28 {
  """Request or reply for the current HWC emergency stop state.
  The command may change by an emergency action, or by a press of the emergency stop button on the HWC."""

  bool active = 1;
  nested Subscription subscription = 2;

  => emergency.Request()
    """emergency request => emergency command reply""";
  <= emergency.Reply(active)
    """emergency reply""";
  => emergency.Subscribe(subscription)
    """emergency subscribe => ack""";
  <- emergency.Update(active, subscription)
    """emergency update""";
}

io.IOAction io_action = 2 {
  """Changes the configuration of one IO port"""

  uint32 port = 1;
  IOFlags flags = 2;

  => io.Action(port, flags)
    """IO action => ack""";
}

io.IOCommandMessage io_command_message = 16 {
  """Request or reply for one IO port's current command. The command may change by an IO action."""

  uint32 port = 1;
  IOFlags flags = 2;
  nested Subscription subscription = 3;

  => io.CommandRequest(port)
    """IO command request => IO command reply""";
  <= io.CommandReply(port, flags)
    """IO command reply""";
  => io.CommandSubscribe(port, subscription)
    """IO command subscribe => ack""";
  <- io.CommandUpdate(port, flags, subscription)
    """IO command update""";
}

io.AnalogMessage analog_message = 3 {
  """Request or reply for one analog sensor's value"""

  uint32 port = 1;
  uint32 value = 2;
  nested Subscription subscription = 3;

  => analog.Request(port)
    """analog request => analog reply""";
  <= analog.Reply(port, value)
    """analog reply""";
  => analog.Subscribe(port, subscription)
    """analog subscribe => ack""";
  <- analog.Update(port, value, subscription)
    """analog update""";
}

io.DigitalMessage digital_message = 4 {
  """Request or reply for one digital sensor's value"""

  uint32 port = 1;
  bool value = 2;
  nested Subscription subscription = 3;

  => digital.Request(port)
    """digital request => digital reply""";
  <= digital.Reply(port, value)
    """digital reply""";
  => digital.Subscribe(port, subscription)
    """digital subscribe => ack""";
  <- digital.Update(port, value, subscription)
    """digital update""";
}

imu.ImuMessage imu_message = 9 {
  """Request or reply for Hedgehog's IMU.
  A request queries either the `RATE``, `ACCELERATION`, or `POSE` of the IMU,
  and the result contains data for all three axes."""

  ImuKind kind = 1;
  sint32 x = 2;
  sint32 y = 3;
  sint32 z = 4;
  nested Subscription subscription = 5;

  => imu.RateRequest()
    """IMU rate request => IMU rate reply""";
  <= imu.RateReply(x, y, z)
    """IMU rate reply""";
  => imu.RateSubscribe(subscription)
    """IMU rate subscribe => ack""";
  <- imu.RateUpdate(x, y, z, subscription)
    """IMU rate update""";

  => imu.AccelerationRequest()
    """IMU acceleration request => IMU acceleration reply""";
  <= imu.AccelerationReply(x, y, z)
    """IMU acceleration reply""";
  => imu.AccelerationSubscribe(subscription)
    """IMU acceleration subscribe => ack""";
  <- imu.AccelerationUpdate(x, y, z, subscription)
    """IMU acceleration update""";

  => imu.PoseRequest()
    """IMU pose request => IMU pose reply""";
  <= imu.PoseReply(x, y, z)
    """IMU pose reply""";
  => imu.PoseSubscribe(subscription)
    """IMU pose subscribe => ack""";
  <- imu.PoseUpdate(x, y, z, subscription)
    """IMU pose update""";
}

motor.MotorAction motor_action = 5 {
  """Command for one motor. By setting a relative or absolute goal position,
the motor will go into `reached_state` upon reaching the position."""

  uint32 port = 1;
  MotorState state = 2;
  sint32 amount = 3;
  MotorState reached_state = 4;
  oneof position {
    sint32 relative = 5;
    sint32 absolute = 6;
  }

  => motor.Action(port, state, amount, [reached_state, relative/absolute])
    """motor action => ack""";
}

motor.MotorConfigAction motor_config_action = 24 {
  """Configures one motor for DC, encoder, or stepper operation.
Stepper configuration requires two motor ports to be combined into one stepper
motor and thus only works for even motor ports (i.e. 0 and 2)."""

  uint32 port = 1;
  oneof config {
    nested DcConfig dc = 2;
    nested EncoderConfig encoder = 3;
    nested StepperConfig stepper = 4;
  }

  => motor.ConfigAction(port, config)
    """motor config action => ack""";
}

motor.MotorCommandMessage motor_command_message = 17 {
  """Request or reply for one motor's current command.
The command may change by a motor action, or by reaching the goal position of a terminating motor action."""

  uint32 port = 1;
  oneof config {
    nested DcConfig dc = 5;
    nested EncoderConfig encoder = 6;
    nested StepperConfig stepper = 7;
  }
  MotorState state = 2;
  sint32 amount = 3;
  nested Subscription subscription = 4;

  => motor.CommandRequest(port)
    """motor command request => motor command reply""";
  <= motor.CommandReply(port, config, state, amount)
    """motor command reply""";
  => motor.CommandSubscribe(port, subscription)
    """motor command subscribe => ack""";
  <- motor.CommandUpdate(port, config, state, amount, subscription)
    """motor command update""";
}

motor.MotorStateMessage motor_state_message = 6 {
  """Request or reply for one motor's state. The motor state may generally change at any time,
but returned values are approximations, and motor state requests may not be supported at all."""

  uint32 port = 1;
  sint32 velocity = 2;
  sint32 position = 3;
  nested Subscription subscription = 4;

  => motor.StateRequest(port)
    """motor state request => motor state reply""";
  <= motor.StateReply(port, velocity, position)
    """motor state reply""";
  => motor.StateSubscribe(port, subscription)
    """motor state subscribe => ack""";
  <- motor.StateUpdate(port, velocity, position, subscription)
    """motor state update""";
}

motor.MotorSetPositionAction motor_set_position_action = 18 {
  """Set one motor's position counter."""

  uint32 port = 1;
  sint32 position = 2;

  => motor.SetPositionAction(port, position)
    """set motor position action => ack""";
}

servo.ServoAction servo_action = 7 {
  """Set one servo target position"""

  uint32 port = 1;
  bool active = 2;
  uint32 position = 3;

  => servo.Action(port, active, position)
    """servo action => ack""";
}

servo.ServoCommandMessage servo_command_message = 19 {
  """Request or reply for one servo's current command. The command may change by a servo action,
actual servo movement is not reflected."""

  uint32 port = 1;
  bool active = 2;
  uint32 position = 3;
  nested Subscription subscription = 4;

  => servo.CommandRequest(port)
    """servo command request => servo command reply""";
  <= servo.CommandReply(port, active, position)
    """servo command reply""";
  => servo.CommandSubscribe(port, subscription)
    """servo command subscribe => ack""";
  <- servo.CommandUpdate(port, active, position, subscription)
    """servo command update""";
}

process.ProcessExecuteAction process_execute_action = 20 {
  """Invoke a process on the controller"""

  repeated string args = 2;
  string working_dir = 1;

  => process.ExecuteAction(*args, [working_dir])
    """process execute action => process execute reply""";
}

process.ProcessExecuteReply process_execute_reply = 21 {
  """Reply with the process' PID for communication purposes"""

  uint32 pid = 1;

  <= process.ExecuteReply(pid)
    """process execute reply""";
}

process.ProcessStreamMessage process_stream_message = 8 {
  """Send data to/receive data from a process' file streams.
The `STDIN` fileno may only be used for sending data to the process,
the others only for receiving data from the process.
An empty chunk means `EOF`."""

  uint32 pid = 1;
  ProcessFileno fileno = 2;
  bytes chunk = 3;

  => process.StreamAction(pid, fileno, chunk)
    """stream data action => ack""";
  <- process.StreamUpdate(pid, fileno, chunk)
    """stream data update""";
}

process.ProcessSignalAction process_signal_action = 22 {
  """Send signal to a process"""

  uint32 pid = 1;
  uint32 signal = 2;

  => process.SignalAction(pid, signal)
    """process signal action => ack""";
}

process.ProcessExitUpdate process_exit_update = 23 {
  """Signals that the process has exited.
A negative value `-N` indicates that the child was terminated by signal `N`."""

  uint32 pid = 1;
  int32 exit_code = 2;

  <- process.ExitUpdate(pid, exit_code)
    """process exit update""";
}

speaker.SpeakerAction speaker_action = 25 {
  """Sets the speaker frequency in Hertz; a frequency of zero turns the speaker off.
Only a range of frequencies may be valid depending on the Hardware."""

  uint32 frequency = 1;

  => speaker.Action(frequency)
    """servo action => ack""";
}

vision.VisionCameraAction vision_camera_action = 29 {
  """Opens or closes the camera. When the camera is open,
frames will be read at the camera's speed and retrieved/analyzed at the client's speed.
The server keeps track which clients are connected: a client opening the camera twice is an error,
a client closing the camera multiple times or without opening it is redundant.
If the camera fails (e.g. USB is disconnected) all clients are disconnected."""

  bool open = 1;

  => vision.OpenCameraAction()
    """open camera action => ack""";
  => vision.CloseCameraAction()
    """close camera action => ack""";
}

vision.VisionChannelMessage vision_channel_message = 30 {
  """Creates, reads, updates or deletes channels. Using an existing key when creating,
or using a nonexistent key unless creating a channel, will result in the command to fail.
On a failure, no change will have been processed.
When reading or deleting a channel, only the key is used and specifying the channel details
is not necessary.

Not specifying any keys in a channel request will list all existing channels."""

  ChannelOperation op = 1;
  repeated Channel channels = 2;

  => vision.CreateChannelAction(channels)
    """create channel action => ack""";
  => vision.UpdateChannelAction(channels)
    """update channel action => ack""";
  => vision.DeleteChannelAction(keys)
    """delete channel action => ack""";
  => vision.ChannelRequest(keys)
    """channel request => channel reply""";
  <= vision.ChannelReply(channels)
    """channel reply""";
}

vision.VisionCaptureFrameAction vision_capture_frame_action = 10 {
  """Decodes and retrieved the latest grabbed frame.
While frames arrive regularly, they are only retrieved on demand with this command.
Subsequent processing is done on this frame, until a newer one is captured."""

  => vision.CaptureFrameAction()
    """capture frame action => ack""";
}

vision.VisionFrameMessage vision_frame_message = 31 {
  """Request or reply for the last captured frame, as a jpg encoded image,
optionally with highlighting for one of the channels added in the image."""

  string highlight = 1;
  bytes frame = 2;

  => vision.FrameRequest(highlight)
    """frame request => frame reply""";
  <= vision.FrameReply(highlight, frame)
    """frame reply""";
}

vision.VisionFeatureMessage vision_feature_message = 11 {
  """Request or reply for the last captured frame's features corresponding to one channel.."""

  string channel = 1;
  nested Feature feature = 2;

  => vision.FeatureRequest(channel)
    """feature request => feature reply""";
  <= vision.FeatureReply(channel, feature)
    """feature reply""";
}
